@heroiclands/package-build 3.3.0 → 4.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 +179 -0
- package/CONTENT.md +29 -12
- package/MIGRATING.md +47 -0
- package/engine/base-compiler.mjs +33 -36
- package/engine/content-links.mjs +15 -13
- package/engine/content-package.mjs +3 -2
- package/engine/field-reference.mjs +2 -2
- package/engine/frontmatter-lint.mjs +26 -0
- package/engine/helpers.mjs +10 -12
- package/engine/index.mjs +4 -1
- package/engine/journals.mjs +2 -3
- package/engine/macros.mjs +2 -3
- package/engine/manifest-emit.mjs +15 -11
- package/engine/note-package.mjs +75 -68
- package/engine/pack-router.mjs +3 -3
- package/engine/retired-fields.mjs +123 -0
- package/engine/scenes.mjs +7 -11
- package/engine/site-build.mjs +30 -11
- package/engine/site-index.mjs +5 -5
- package/package.json +1 -1
- package/sohl/actors.mjs +2 -3
- package/sohl/item-fields.mjs +0 -8
- package/sohl/items.mjs +2 -3
- package/types/engine/base-compiler.d.mts +12 -17
- package/types/engine/content-package.d.mts +3 -2
- package/types/engine/helpers.d.mts +5 -8
- package/types/engine/index.d.mts +1 -0
- package/types/engine/manifest-emit.d.mts +7 -8
- package/types/engine/note-package.d.mts +29 -34
- package/types/engine/pack-router.d.mts +3 -3
- package/types/engine/retired-fields.d.mts +54 -0
- package/types/engine/site-build.d.mts +10 -0
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
* The tallies one pass accumulates while walking the tree.
|
|
3
3
|
*
|
|
4
4
|
* `declined` and `skippedOther` are deliberately separate numbers. A declined
|
|
5
|
-
* note is one this build **refused** — it
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* note is one this build **refused** — it declares a retired frontmatter field
|
|
6
|
+
* — and it is an error; a skipped one legitimately belongs to another pass, and
|
|
7
|
+
* there are thousands of those. Folding the first into the second is what let a
|
|
8
|
+
* whole tree be filtered out in silence (#56).
|
|
9
9
|
*
|
|
10
10
|
* @typedef {object} PassStats
|
|
11
11
|
* @property {number} compiled - Notes that became a document.
|
|
12
|
-
* @property {number} skippedDraft - Notes marked `draft: true`.
|
|
13
12
|
* @property {number} skippedNoId - Notes with no `id`, where that is tolerated.
|
|
14
13
|
* @property {number} skippedOther - Notes this pass does not claim.
|
|
15
|
-
* @property {number} declined - Notes refused because they declare
|
|
16
|
-
*
|
|
14
|
+
* @property {number} declined - Notes refused because they declare a retired
|
|
15
|
+
* frontmatter field. Counted as errors, never as skips.
|
|
17
16
|
*/
|
|
18
17
|
/**
|
|
19
18
|
* The shared walk → filter → expand → convert → build → write → count loop.
|
|
@@ -310,20 +309,16 @@ export class BasePackCompiler {
|
|
|
310
309
|
* The tallies one pass accumulates while walking the tree.
|
|
311
310
|
*
|
|
312
311
|
* `declined` and `skippedOther` are deliberately separate numbers. A declined
|
|
313
|
-
* note is one this build **refused** — it
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
312
|
+
* note is one this build **refused** — it declares a retired frontmatter field
|
|
313
|
+
* — and it is an error; a skipped one legitimately belongs to another pass, and
|
|
314
|
+
* there are thousands of those. Folding the first into the second is what let a
|
|
315
|
+
* whole tree be filtered out in silence (#56).
|
|
317
316
|
*/
|
|
318
317
|
export type PassStats = {
|
|
319
318
|
/**
|
|
320
319
|
* - Notes that became a document.
|
|
321
320
|
*/
|
|
322
321
|
compiled: number;
|
|
323
|
-
/**
|
|
324
|
-
* - Notes marked `draft: true`.
|
|
325
|
-
*/
|
|
326
|
-
skippedDraft: number;
|
|
327
322
|
/**
|
|
328
323
|
* - Notes with no `id`, where that is tolerated.
|
|
329
324
|
*/
|
|
@@ -333,8 +328,8 @@ export type PassStats = {
|
|
|
333
328
|
*/
|
|
334
329
|
skippedOther: number;
|
|
335
330
|
/**
|
|
336
|
-
* - Notes refused because they declare
|
|
337
|
-
*
|
|
331
|
+
* - Notes refused because they declare a retired
|
|
332
|
+
* frontmatter field. Counted as errors, never as skips.
|
|
338
333
|
*/
|
|
339
334
|
declined: number;
|
|
340
335
|
};
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* `package:` frontmatter and the compilers kept the ones that matched. Every
|
|
13
13
|
* content tree is single-package — each is single-sourced in the repository that
|
|
14
14
|
* ships it — so the field restated this constant once per note while a value
|
|
15
|
-
* that matched nothing filtered the whole tree out in silence.
|
|
16
|
-
*
|
|
15
|
+
* that matched nothing filtered the whole tree out in silence. That field is
|
|
16
|
+
* retired and declaring it now fails the build; this value stays, here, where
|
|
17
|
+
* it is declared once.
|
|
17
18
|
*
|
|
18
19
|
* Stable across compilation targets. If this content were ever compiled for a
|
|
19
20
|
* second game system, it would still be published as `sohl` — only the Foundry
|
|
@@ -274,17 +274,15 @@ export function collectContentDocs(contentBase: string): Array<{
|
|
|
274
274
|
* Expand the fenced `dataview` tables in one note's markdown, before wikilinks
|
|
275
275
|
* are resolved — so a generated cell may itself be a wikilink.
|
|
276
276
|
*
|
|
277
|
-
* A table searches
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* unswept — or every swept — note from the table (#56).
|
|
277
|
+
* A table searches the whole tree, which is one package's notes and nothing
|
|
278
|
+
* else — so there is no longer a package to scope on. It used to filter, back
|
|
279
|
+
* when a tree could hold several packages' notes and `package:` said which was
|
|
280
|
+
* which; that field is retired and the filter with it (#56).
|
|
282
281
|
*
|
|
283
282
|
* @param {string} body - The note's markdown body.
|
|
284
283
|
* @param {object} ctx
|
|
285
284
|
* @param {Array<object>} ctx.docs - From {@link collectContentDocs}.
|
|
286
285
|
* @param {string} ctx.name - The note, for the error message.
|
|
287
|
-
* @param {string} [ctx.pkg] - The source note's package.
|
|
288
286
|
* @param {object} [ctx.fm] - The source note's frontmatter, which is what a
|
|
289
287
|
* query's `this` reads. Its entry in `docs` supplies the path as well.
|
|
290
288
|
* @param {number} [ctx.bodyLine] - 1-based file line of the body's first line,
|
|
@@ -297,10 +295,9 @@ export function collectContentDocs(contentBase: string): Array<{
|
|
|
297
295
|
* compile rather than shipping a table-shaped hole. The error carries
|
|
298
296
|
* `position`, the directive's own line.
|
|
299
297
|
*/
|
|
300
|
-
export function expandNoteTables(body: string, { docs, name,
|
|
298
|
+
export function expandNoteTables(body: string, { docs, name, fm, bodyLine }: {
|
|
301
299
|
docs: Array<object>;
|
|
302
300
|
name: string;
|
|
303
|
-
pkg?: string | undefined;
|
|
304
301
|
fm?: object | undefined;
|
|
305
302
|
bodyLine?: number | undefined;
|
|
306
303
|
}): {
|
package/types/engine/index.d.mts
CHANGED
|
@@ -6,6 +6,7 @@ export * as packConfig from "./pack-config.mjs";
|
|
|
6
6
|
export * as packRouter from "./pack-router.mjs";
|
|
7
7
|
export * as contentPackage from "./content-package.mjs";
|
|
8
8
|
export * as notePackage from "./note-package.mjs";
|
|
9
|
+
export * as retiredFields from "./retired-fields.mjs";
|
|
9
10
|
export * as contentSlug from "./content-slug.mjs";
|
|
10
11
|
export * as contentAddress from "./content-address.mjs";
|
|
11
12
|
export * as foreignManifests from "./foreign-manifests.mjs";
|
|
@@ -37,14 +37,13 @@ export function entriesForNote(fm: object, name: string, address: string, body:
|
|
|
37
37
|
/**
|
|
38
38
|
* Every note this package publishes, as manifest entries.
|
|
39
39
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* filtered out of a manifest that then claimed the package published nothing.
|
|
40
|
+
* Every note in the tree is this package's, so nothing here selects by package:
|
|
41
|
+
* the key's first segment is `contentPackage` (#56). A note still declaring the
|
|
42
|
+
* retired `package:` or `draft:` field **throws** rather than being skipped —
|
|
43
|
+
* skipping one silently is how a whole tree came to be filtered out of a
|
|
44
|
+
* manifest that then claimed the package published nothing, and it is what let
|
|
45
|
+
* a drafted note's inbound links look like links to a note that never existed
|
|
46
|
+
* (#69).
|
|
48
47
|
*
|
|
49
48
|
* A note that has no address is **reported, not guessed** — the finding carries
|
|
50
49
|
* the file and the reason, so a caller can print it or fail on it. Inventing an
|
|
@@ -1,54 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Non-validating: the answer for a note that declares nothing, and for one that
|
|
5
|
-
* declares the configured package, is the same value. A note declaring some
|
|
6
|
-
* *other* package is answered literally here rather than corrected — the
|
|
7
|
-
* compile pass reports that, once, through {@link assertNotePackage}.
|
|
8
|
-
*
|
|
9
|
-
* @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
|
|
10
|
-
* could not be parsed.
|
|
11
|
-
* @param {string} [configured] - The package this build compiles. Defaults to
|
|
12
|
-
* the configured `contentPackage`; passed explicitly by callers that already
|
|
13
|
-
* carry it in a context object, so a caller's configuration drives every read.
|
|
14
|
-
* @returns {string} The package.
|
|
15
|
-
*/
|
|
16
|
-
export function notePackage(fm: object | null | undefined, configured?: string): string;
|
|
17
|
-
/**
|
|
18
|
-
* A note's frontmatter as a generated table searches it — its package present
|
|
19
|
-
* whether or not the note declares one.
|
|
2
|
+
* A note's frontmatter as a generated table searches it — its package present,
|
|
3
|
+
* though no note declares one.
|
|
20
4
|
*
|
|
21
5
|
* A `dataview` query resolves `package` out of frontmatter like any other
|
|
22
6
|
* field, so a collection note that scopes itself with `WHERE … and package =
|
|
23
|
-
* "sohl"`
|
|
24
|
-
* table** in silence.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* way.
|
|
7
|
+
* "sohl"` would match nothing now that the field is gone, and would render an
|
|
8
|
+
* **empty table** in silence. Supplying the derived value here is what kept the
|
|
9
|
+
* sweep mechanical rather than a trap (#56) — and a query that never mentions
|
|
10
|
+
* `package` is unaffected either way.
|
|
28
11
|
*
|
|
29
|
-
* The
|
|
30
|
-
*
|
|
12
|
+
* The frontmatter is copied rather than written into: it is the note's own
|
|
13
|
+
* parsed object, shared with every other reader, and the derived package is a
|
|
14
|
+
* property of *this search*, not of the note.
|
|
31
15
|
*
|
|
32
16
|
* @param {object|null|undefined} fm - Parsed frontmatter.
|
|
33
|
-
* @param {string} [configured] - The package this build compiles.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
17
|
+
* @param {string} [configured] - The package this build compiles. Defaults to
|
|
18
|
+
* the configured `contentPackage`; passed explicitly by callers that already
|
|
19
|
+
* carry it in a context object, so a caller's configuration drives every read.
|
|
20
|
+
* @returns {object|null|undefined} A shallow copy carrying the derived package,
|
|
21
|
+
* or whatever was passed when it is not frontmatter at all.
|
|
36
22
|
*/
|
|
37
23
|
export function searchableFrontmatter(fm: object | null | undefined, configured?: string): object | null | undefined;
|
|
38
24
|
/**
|
|
39
|
-
*
|
|
25
|
+
* Refuse a note that declares `package:` at all.
|
|
40
26
|
*
|
|
41
|
-
*
|
|
27
|
+
* Presence is the whole test — a declaration that *agrees* with the
|
|
28
|
+
* configuration is as retired as one that disagrees, and an empty one
|
|
29
|
+
* (`package:`, which parses as `null`) is still the field.
|
|
30
|
+
*
|
|
31
|
+
* @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
|
|
32
|
+
* could not be parsed.
|
|
42
33
|
* @param {object} [options] - Options.
|
|
43
34
|
* @param {string} [options.file] - The note's path, named in the message. Omit
|
|
44
35
|
* it where the caller emits through a diagnostic, which puts the locator at
|
|
45
36
|
* the start of the line already — repeating it prints the path twice.
|
|
37
|
+
* @param {string} [options.absPath] - The note's file on disk, read only on the
|
|
38
|
+
* failing path to locate the offending line and column. The position rides on
|
|
39
|
+
* the thrown error as `position`, for a caller that emits a diagnostic.
|
|
46
40
|
* @param {string} [options.configured] - The package this build compiles.
|
|
47
41
|
* Defaults to the configured `contentPackage`.
|
|
48
|
-
* @returns {
|
|
49
|
-
* @throws {Error} When the note declares
|
|
42
|
+
* @returns {void}
|
|
43
|
+
* @throws {Error} When the note declares the field.
|
|
50
44
|
*/
|
|
51
|
-
export function
|
|
45
|
+
export function assertNoDeclaredPackage(fm: object | null | undefined, { file, absPath, configured }?: {
|
|
52
46
|
file?: string | undefined;
|
|
47
|
+
absPath?: string | undefined;
|
|
53
48
|
configured?: string | undefined;
|
|
54
|
-
}):
|
|
49
|
+
}): void;
|
|
@@ -46,9 +46,9 @@ export class PackRoutingError extends Error {
|
|
|
46
46
|
/**
|
|
47
47
|
* The frontmatter field a note declares its pack in.
|
|
48
48
|
*
|
|
49
|
-
* Deliberately close to the
|
|
49
|
+
* Deliberately close to the retired `package:` and deliberately not the same
|
|
50
50
|
* word: `package:` said which *distribution* owned a note — now the
|
|
51
|
-
* repository's `contentPackage
|
|
52
|
-
* receives its document.
|
|
51
|
+
* repository's `contentPackage`, and no longer authorable (#56) — while `pack:`
|
|
52
|
+
* says which *compendium* receives its document.
|
|
53
53
|
*/
|
|
54
54
|
export const PACK_FIELD: "pack";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a note declaring `draft:` is told, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Written once and shared by the compile-time refusal and the frontmatter lint,
|
|
5
|
+
* because an author meets whichever of the two runs first and they should read
|
|
6
|
+
* the same. It says what the field did and what to write instead, rather than
|
|
7
|
+
* which value to correct: no value makes declaring it right.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} [file] - The note's path, named in the message. Omit it where
|
|
10
|
+
* the caller emits through a diagnostic, whose locator already starts the
|
|
11
|
+
* line — repeating it prints the path twice.
|
|
12
|
+
* @returns {string} The message, unpunctuated at the end as a finding is.
|
|
13
|
+
*/
|
|
14
|
+
export function draftRetiredMessage(file?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Refuse a note that declares `draft:` at all.
|
|
17
|
+
*
|
|
18
|
+
* Presence is the whole test. `draft: false` is as retired as `draft: true` —
|
|
19
|
+
* it reads as "publish this note", which is what happens either way, and is
|
|
20
|
+
* exactly the belief the message exists to correct.
|
|
21
|
+
*
|
|
22
|
+
* @param {object|null|undefined} fm - Parsed frontmatter, or nothing when it
|
|
23
|
+
* could not be parsed.
|
|
24
|
+
* @param {object} [options] - Options.
|
|
25
|
+
* @param {string} [options.file] - The note's path, named in the message. Omit
|
|
26
|
+
* it where the caller emits through a diagnostic, which puts the locator at
|
|
27
|
+
* the start of the line already — repeating it prints the path twice.
|
|
28
|
+
* @param {string} [options.absPath] - The note's file on disk, read only on the
|
|
29
|
+
* failing path to locate the offending line and column. The position rides on
|
|
30
|
+
* the thrown error as `position`, for a caller that emits a diagnostic.
|
|
31
|
+
* @returns {void}
|
|
32
|
+
* @throws {Error} When the note declares the field.
|
|
33
|
+
*/
|
|
34
|
+
export function assertNoDraftField(fm: object | null | undefined, { file, absPath }?: {
|
|
35
|
+
file?: string | undefined;
|
|
36
|
+
absPath?: string | undefined;
|
|
37
|
+
}): void;
|
|
38
|
+
/**
|
|
39
|
+
* A frontmatter key's position in a note's file, or nothing.
|
|
40
|
+
*
|
|
41
|
+
* {@link positionInFrontmatter} answers the same question from the file's
|
|
42
|
+
* *text*; this reads the file to ask it. Kept apart from either caller because
|
|
43
|
+
* both refusals need it and a second copy is a second thing to keep correct.
|
|
44
|
+
*
|
|
45
|
+
* @param {string|undefined} absPath - The note's file.
|
|
46
|
+
* @param {string} key - The top-level frontmatter key.
|
|
47
|
+
* @returns {{line?: number, column?: number}|undefined} Spreadable position
|
|
48
|
+
* fields, dropped rather than guessed when the file cannot be read or the key
|
|
49
|
+
* cannot be found — as `formatDiagnostic` requires.
|
|
50
|
+
*/
|
|
51
|
+
export function locateFrontmatterKey(absPath: string | undefined, key: string): {
|
|
52
|
+
line?: number;
|
|
53
|
+
column?: number;
|
|
54
|
+
} | undefined;
|
|
@@ -84,6 +84,16 @@ export function tableUniverse(pages: object[]): Map<string, object[]>;
|
|
|
84
84
|
* redirect stub at each name. They are dropped, and this build emits no
|
|
85
85
|
* redirects of its own.
|
|
86
86
|
*
|
|
87
|
+
* A content page carries the package the build **derived** (#65). No note
|
|
88
|
+
* declares one — `package:` is retired (#56) — so the note's frontmatter alone
|
|
89
|
+
* would publish a page that does not say which package it belongs to. The
|
|
90
|
+
* emitted page is what a
|
|
91
|
+
* theme reads: `breadcrumbs.html` builds its middle crumb from
|
|
92
|
+
* `.Params.package`, so without it that crumb degrades from a linked, labelled
|
|
93
|
+
* section to a bare type slug. Writing the derived value keeps a page
|
|
94
|
+
* self-describing and makes sweeping the field out of a content tree
|
|
95
|
+
* output-preserving for a site as it already is for the packs.
|
|
96
|
+
*
|
|
87
97
|
* @param {object} page - The page.
|
|
88
98
|
* @param {object} options - `{ sections, readmeSections, decorate }`.
|
|
89
99
|
* @returns {object} The frontmatter to write.
|