@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.
@@ -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 names a package this repository does
6
- * not compile — and it is an error; a skipped one legitimately belongs to
7
- * another pass, and there are thousands of those. Folding the first into the
8
- * second is what let a whole tree be filtered out in silence (#56).
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 another
16
- * package. Counted as errors, never as skips.
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 names a package this repository does
314
- * not compile — and it is an error; a skipped one legitimately belongs to
315
- * another pass, and there are thousands of those. Folding the first into the
316
- * second is what let a whole tree be filtered out in silence (#56).
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 another
337
- * package. Counted as errors, never as skips.
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. The field is
16
- * being retired; the value stays, here, where it is declared once.
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 only notes of the source note's own package, so a SoHL page
278
- * never tabulates setting-package content (and vice versa). Each candidate's
279
- * package is **derived** rather than read out of its frontmatter: `package:` is
280
- * optional, and comparing a declared value with an absent one would drop every
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, pkg, fm, bodyLine }: {
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
  }): {
@@ -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
- * Drafts are excluded because the site does not publish them, and an entry for
41
- * an unpublished page is exactly the dead link the manifest exists to prevent.
42
- *
43
- * Every note in the tree is this package's note, whether or not it says so:
44
- * `package:` is optional and merely has to agree (#56). A note naming a
45
- * different package **throws** rather than being skipped this build is not
46
- * authoritative for it, and skipping it silently is how a whole tree came to be
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
- * The package a note belongs to.
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"` matches nothing once the field is deleted, and renders an **empty
24
- * table** in silence. Deriving the value here keeps the two spellings
25
- * equivalent, so a sweep that deletes the field is mechanical rather than a
26
- * trap (#56) — and a query that never mentions `package` is unaffected either
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 declared value is left alone when there is one, so nothing about an
30
- * unswept tree changes.
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
- * @returns {object|null|undefined} The frontmatter itself when it declares a
35
- * package, else a shallow copy carrying the derived one.
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
- * The package a note belongs to, refusing one that names another package.
25
+ * Refuse a note that declares `package:` at all.
40
26
  *
41
- * @param {object|null|undefined} fm - Parsed frontmatter.
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 {string} The package, which is always `configured`.
49
- * @throws {Error} When the note declares a different package.
42
+ * @returns {void}
43
+ * @throws {Error} When the note declares the field.
50
44
  */
51
- export function assertNotePackage(fm: object | null | undefined, { file, configured }?: {
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
- }): string;
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 retiring `package:` and deliberately not the same
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` (#56) — while `pack:` says which *compendium*
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.