@heroiclands/package-build 11.0.0 → 11.1.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 CHANGED
@@ -1,5 +1,101 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 11.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8906d7a: **A `README` landing's `subType` is checked against the sections the repository declares, not the genre list.**
8
+
9
+ Two vocabularies were spelled with one key. For every note, `subType` is a
10
+ sub-kind of its type, checked against a closed list. For a `README` under
11
+ `publish.address.landing: readme` it is additionally the **URL section** —
12
+ `sectionOf` reads a `doc`'s `subType` as the segment the landing addresses — and
13
+ that is an open set the consuming repository names in `site.sections` /
14
+ `site.readmeSections`. `engine/frontmatter-lint.mjs` applied the closed reading
15
+ while `engine/content-address.mjs` applied the open one, so the two halves of the
16
+ toolchain disagreed about the same note: `content-build site` addressed and
17
+ published `Weapons/README.md` at `weapongear/`, and `content-build lint` refused
18
+ it because `doc` declares only `rules`, `user-guide` and `reference`. An item
19
+ section's landing was not expressible.
20
+
21
+ The check now widens **for a landing only**, and only under `landing: readme`: a
22
+ `README` whose `subType` is its section may name any section the repository
23
+ declares, as well as any of its type's own genres — a genre is a section a `doc`
24
+ tree publishes under whether or not the repository describes it, so narrowing to
25
+ the configured set alone would refuse a correct `README`. An ordinary note's
26
+ `subType` stays closed to its type's genres, unchanged.
27
+
28
+ **The guard survives**, checked against the set that actually decides where the
29
+ page goes:
30
+
31
+ ```text
32
+ Weapons/README.md:4:1: error: `subType` "weapongeer" is the section this README
33
+ lands at, and nothing declares it: it is neither a section this repository
34
+ configures under `site.sections` / `site.readmeSections` (rules, user-guide,
35
+ weapongear, …) nor one of the subtypes doc declares (rules, user-guide,
36
+ reference). Did you mean "weapongear"?
37
+ ```
38
+
39
+ Whether the value is an address is asked of `sectionOf` rather than by naming a
40
+ type, so the linter still knows no type names of its own; the section list is
41
+ read from the resolved configuration the site build renders those landings from,
42
+ through a new `declaredSections(config)`, so neither can name a section the other
43
+ does not.
44
+
45
+ _Minor rather than patch_: `lintNote` and `lintFrontmatter` take two new
46
+ options (`landing`, `sections`) and `content-config.mjs` exports
47
+ `declaredSections`. Both options default to today's behaviour, so a caller that
48
+ passes neither — and a repository that declares no sections — is unaffected.
49
+
50
+ Closes #197
51
+
52
+ ### Patch Changes
53
+
54
+ - 68910b6: **A `README` landing may name any section that exists, not only a configured one.**
55
+
56
+ #197 widened a landing's `subType` check by one term and it was the wrong one:
57
+ the sections the repository configures in `site.sections` /
58
+ `site.readmeSections`. That keys on configuration a consumer may legitimately not
59
+ have. `sohl-thalorna` has **no `site:` block at all** — it renders its site
60
+ through a local fork of the emitter — so `declaredSections` answered `[]`, the
61
+ accepted set collapsed back to the three `doc` genres, and five of its landings
62
+ were still refused for naming their own content type:
63
+
64
+ ```text
65
+ assets/content/Characters/README.md:7:1: error: `subType` "being" is not one of the subtypes doc declares (rules, user-guide, reference)
66
+ ```
67
+
68
+ A landing's `subType` is an **address**, so it is now checked against the
69
+ addresses that exist — the range of `sectionOf` over every note the format
70
+ permits, plus whatever the repository names:
71
+
72
+ 1. **Every content type the specification declares.** `sectionOf` returns
73
+ `fm.type` for a non-`doc` note, so `being`, `lore`, `scenario` and
74
+ `weapongear` are sections _by construction_, configured or not.
75
+ 2. **The type's own subtypes** — `rules`, `user-guide`, `reference`.
76
+ 3. **The configured sections**, which may name one that is neither: `sohl` has
77
+ `credits` and `dev-docs`.
78
+
79
+ **The guard survives.** A misspelling is in none of the three, so it still fails,
80
+ and the near miss is drawn from the whole union. What is deliberately no longer
81
+ caught is a landing for a section that exists but is currently empty — which is
82
+ legitimate, and is why the set is the types the format _declares_ rather than
83
+ those _present in the tree_: `sohl` ships two such landings above tables that
84
+ stay empty until the first note of each type does.
85
+
86
+ **The type list is the specification's, not the schema map's.** `NOTE_SCHEMAS`
87
+ declares 18 types and `docs/content-format.md` declares 23; `lore`, `place`,
88
+ `scenario`, `vehicle` and `armorlocation` are in the second only. Checking an
89
+ address against the schema map would refuse `Lore/README.md` for a reason that
90
+ is not about addresses, and would report one gap twice in two vocabularies.
91
+ Whether a note's fields can be checked is a different question, answered
92
+ elsewhere and reported on the notes themselves.
93
+
94
+ `lintNote` / `lintFrontmatter` take a `types` option beside `sections`; both
95
+ default to empty, so a caller that passes neither is unaffected.
96
+
97
+ Closes #200
98
+
3
99
  ## 11.0.0
4
100
 
5
101
  ### Major Changes
package/CONTENT.md CHANGED
@@ -1252,6 +1252,22 @@ publish:
1252
1252
  The two are alternatives rather than a pair that could both apply: each live
1253
1253
  content tree holds notes the other rule would move.
1254
1254
 
1255
+ **Under `readme`, a landing's `subType` is an address rather than a genre.** The
1256
+ segment the `README` lands at is what `sectionOf` reads — for a `doc`, its
1257
+ `subType` — so `Weapons/README.md` writes `subType: weapongear` and publishes at
1258
+ `weapongear/`. `content-build lint` checks that value against the sections that
1259
+ can exist, which is three sets: every **content type** the format declares
1260
+ (`sectionOf` returns a non-`doc` note's own type, so `being` and `weapongear` are
1261
+ sections by construction), the **subtypes the type declares** (`rules`,
1262
+ `user-guide`, `reference`), and any section named in
1263
+ [`site.sections` / `site.readmeSections`](#what-a-section-may-declare). A
1264
+ misspelt one is still refused, by name and against all three, with the near miss
1265
+ suggested. This applies to a `README` only: every other note's `subType` stays
1266
+ closed to the values its type declares.
1267
+
1268
+ Configuring the section is **not** a prerequisite — `site.sections` is framing,
1269
+ and a package that renders its own site need declare no `site:` block at all.
1270
+
1255
1271
  A note the scheme yields no address for — one carrying no `shortcode`, a `doc`
1256
1272
  with no subtype (so no section to be filed under), a collection note naming no
1257
1273
  section — is **reported and omitted**, never guessed. The command
@@ -72,6 +72,10 @@ import {
72
72
  import { renderItemFieldReference } from "../engine/field-reference.mjs";
73
73
  import { lintContentTree } from "../engine/content-lint.mjs";
74
74
  import { lintFrontmatter } from "../engine/frontmatter-lint.mjs";
75
+ // The sections this repository declares — the open set a `README` landing's
76
+ // `subType` names (#197). Read from the resolved configuration the site build
77
+ // renders those landings from, so neither can name a section the other does not.
78
+ import { declaredSections } from "../content-config.mjs";
75
79
  import { loadContentFormat } from "../engine/content-format.mjs";
76
80
  import {
77
81
  checkDeclaredFields,
@@ -736,6 +740,22 @@ function lintCommand() {
736
740
  // decides which vocabulary a tree is held to.
737
741
  vocabulary: NOTE_VOCABULARY,
738
742
  references: argv.references,
743
+ // How this repository addresses a section landing, and the
744
+ // sections that can exist — the open vocabulary a
745
+ // `README`'s `subType` is read against (#197, #200).
746
+ // Passed in for the same reason the schemas are: the
747
+ // linter checks a note against what it is handed.
748
+ //
749
+ // The types come from the **specification**, not from
750
+ // `schemas`: a landing's `subType` is an address, and a
751
+ // type the format declares is a section whether or not
752
+ // this build carries a schema to check its notes' fields
753
+ // with. Reading `schemas` here would refuse `Lore/README.md`
754
+ // for a reason that is not about addresses, and report one
755
+ // gap twice in two vocabularies.
756
+ landing: config.publish.address.landing,
757
+ types: [...loadContentFormat().types.keys()],
758
+ sections: declaredSections(config),
739
759
  });
740
760
 
741
761
  // What the builders emit, against what the receiving system
@@ -210,6 +210,34 @@ export function publishesContentPages(config) {
210
210
  return config.publish.site === "content";
211
211
  }
212
212
 
213
+ /**
214
+ * Every URL section this repository names, in declaration order (#197).
215
+ *
216
+ * A section is *declared* by describing it: `site.sections` for one whose
217
+ * landing this build generates, `site.readmeSections` for one whose landing is
218
+ * a `README`. Between them they are the open set of addresses a repository
219
+ * says it publishes under — which is what a `README` landing's `subType` names,
220
+ * since `sectionOf` reads that field as the section rather than as a genre.
221
+ *
222
+ * Read from the same two maps the site build renders each landing from, so the
223
+ * set a note is checked against and the set a landing is written from cannot
224
+ * come to disagree. A repository that describes no section declares none, and
225
+ * the answer is empty rather than a guess assembled from the tree.
226
+ *
227
+ * @param {{site: {sections: object, readmeSections: object}}} config - A
228
+ * resolved configuration.
229
+ * @returns {readonly string[]} The section names, deduplicated.
230
+ */
231
+ export function declaredSections(config) {
232
+ const site = config?.site ?? {};
233
+ return Object.freeze([
234
+ ...new Set([
235
+ ...Object.keys(site.sections ?? {}),
236
+ ...Object.keys(site.readmeSections ?? {}),
237
+ ]),
238
+ ]);
239
+ }
240
+
213
241
  /**
214
242
  * @typedef {"systems" | "modules"} PackageKind
215
243
  */
@@ -55,7 +55,10 @@
55
55
  * @module
56
56
  */
57
57
 
58
+ import path from "node:path";
59
+
58
60
  import { authoredFields } from "./field-spec.mjs";
61
+ import { DEFAULT_ADDRESS_SCHEME, sectionOf } from "./content-address.mjs";
59
62
  import { resolveFieldValue, SYSTEM_BLOCK_KEYS, unknownBlockKeys } from "./system-block.mjs";
60
63
  import { positionInFrontmatter, positionOfFrontmatterPath } from "./diagnostics.mjs";
61
64
  import { checkHomepageAddressFields } from "./homepage.mjs";
@@ -313,22 +316,83 @@ function checkDataContainer(note, { type, fields }) {
313
316
  return findings;
314
317
  }
315
318
 
319
+ /**
320
+ * Whether this note's `subType` names the **section it lands at** rather than a
321
+ * sub-kind of its type (#197).
322
+ *
323
+ * Under `landing: readme` a `README.md` addresses its whole section, and the
324
+ * segment is what `sectionOf` reads — for a `doc`, its `subType`. So that one
325
+ * field is spelled once and read twice: as a genre on an ordinary note, and as
326
+ * an address on a landing. The two vocabularies are not the same set, and the
327
+ * closed one cannot answer for the open one.
328
+ *
329
+ * Asked of {@link sectionOf} rather than by naming a type, because which types
330
+ * route by their `subType` is address knowledge and this module deliberately
331
+ * knows no type names of its own.
332
+ *
333
+ * @param {object} note - The note.
334
+ * @param {string} landing - The repository's landing rule.
335
+ * @returns {boolean} Whether the value is an address.
336
+ */
337
+ function subTypeIsSection(note, landing) {
338
+ if (landing !== "readme") return false;
339
+ const file = typeof note.file === "string" ? note.file : "";
340
+ if (path.basename(file).toLowerCase() !== "readme.md") return false;
341
+ const fm = note.fm ?? {};
342
+ return typeof fm.subType === "string" && sectionOf(fm) === fm.subType;
343
+ }
344
+
316
345
  /**
317
346
  * Check a note's top-level `subType` against the values its type declares
318
- * (#128).
347
+ * (#128), or — for a `README` landing — against the sections that can exist
348
+ * (#197, #200).
319
349
  *
320
350
  * `subType` stays at the top level — it is what each system's map reads to
321
351
  * derive a document type, so it describes the note rather than the subject —
322
352
  * but it is not open like the rest of that region: a type either declares a
323
353
  * `subType` or does not, and a type that does declares its values.
324
354
  *
355
+ * **A landing page's is an address, and an address is checked against the
356
+ * addresses that exist.** A `README` under `landing: readme` addresses its
357
+ * section through this field, and a section is `weapongear` or `being` as
358
+ * readily as it is `rules`. What can legitimately appear there is the range of
359
+ * {@link sectionOf} over every note the format permits, plus whatever the
360
+ * repository names:
361
+ *
362
+ * 1. **Every declared content type.** `sectionOf` returns `fm.type` for a
363
+ * non-`doc` note, so `being`, `lore` and `weapongear` are sections *by
364
+ * construction*, configured or not.
365
+ * 2. **The type's own subtypes** — `rules`, `user-guide`, `reference` for a
366
+ * `doc`, since a `doc` routes by its subtype.
367
+ * 3. **The configured sections**, which may name one that is neither: `sohl`
368
+ * configures `credits` and `dev-docs`.
369
+ *
370
+ * Checking only (3) was #197's fix and keyed on configuration a consumer may
371
+ * legitimately not have: `sohl-thalorna` has no `site:` block at all, so five
372
+ * of its landings were refused for naming their own content type (#200).
373
+ *
374
+ * **The guard survives.** A misspelling is none of the three, so it still
375
+ * fails, with the near miss drawn from the whole union. What is deliberately no
376
+ * longer caught is a landing for a section that exists but is empty — which is
377
+ * legitimate, and is why the set is the types the format *declares* rather than
378
+ * the types *present in the tree*: `sohl` ships two such landings above tables
379
+ * that stay empty until the first note of each type does.
380
+ *
325
381
  * @param {object} note - The note.
326
382
  * @param {object} opts
327
383
  * @param {string} opts.type - The note's type, for the message.
328
384
  * @param {object} opts.entry - The type's vocabulary entry.
385
+ * @param {boolean} [opts.asSection] - Whether the value is a section address —
386
+ * see {@link subTypeIsSection}.
387
+ * @param {readonly string[]} [opts.types] - The content types the format
388
+ * declares. Empty leaves a landing checked against its genres and the
389
+ * configured sections alone.
390
+ * @param {readonly string[]} [opts.sections] - The sections the repository
391
+ * configures. Empty when it configures none, which is ordinary rather than a
392
+ * defect.
329
393
  * @returns {object[]} Findings.
330
394
  */
331
- function checkSubType(note, { type, entry }) {
395
+ function checkSubType(note, { type, entry, asSection = false, types = [], sections = [] }) {
332
396
  const fm = note.fm ?? {};
333
397
  if (!Object.hasOwn(fm, "subType") || fm.subType == null || fm.subType === "") return [];
334
398
 
@@ -353,6 +417,28 @@ function checkSubType(note, { type, entry }) {
353
417
  // the value is nobody's to check yet.
354
418
  if (values == null || values.includes(value)) return [];
355
419
 
420
+ if (asSection) {
421
+ if (types.includes(value) || sections.includes(value)) return [];
422
+ const guess = nearest(value, [...types, ...values, ...sections]);
423
+ return [
424
+ {
425
+ file: note.file,
426
+ ...at,
427
+ severity: "error",
428
+ message:
429
+ `\`subType\` "${value}" is the section this README lands ` +
430
+ `at, and nothing declares it. A section is a content type ` +
431
+ `(${types.join(", ")}), a subtype ${type} declares ` +
432
+ `(${values.join(", ")})` +
433
+ (sections.length ?
434
+ `, or a section configured under \`site.sections\` / ` +
435
+ `\`site.readmeSections\` (${sections.join(", ")})`
436
+ : "") +
437
+ (guess ? `. Did you mean "${guess}"?` : ""),
438
+ },
439
+ ];
440
+ }
441
+
356
442
  const guess = nearest(value, values);
357
443
  return [
358
444
  {
@@ -435,9 +521,33 @@ function checkTags(note, { type }) {
435
521
  * @param {Readonly<Record<string, {known?: readonly string[], fieldVocabulary?: boolean}>>} [opts.systems]
436
522
  * The system blocks to check, and what each accepts. See
437
523
  * {@link DEFAULT_SYSTEM_BLOCKS}.
524
+ * @param {string} [opts.landing] - The repository's landing rule, from
525
+ * `publish.address.landing`. It decides which note addresses a whole section,
526
+ * and so whether a `subType` is a genre or an address (#197).
527
+ * @param {readonly string[]} [opts.types] - The content types the format
528
+ * declares — the sections that exist by construction (#200). Read from
529
+ * `docs/content-format.md` rather than from `schemas`, because the two answer
530
+ * different questions: whether an address is real, and whether this build can
531
+ * check a note's fields. A type the specification declares and no schema
532
+ * covers is a real section, and its notes are reported on their own account.
533
+ * @param {readonly string[]} [opts.sections] - The sections the repository
534
+ * configures, from `declaredSections`. Supplied by the caller for the same
535
+ * reason `vocabulary` is: this module checks a note against what it is
536
+ * handed.
438
537
  * @returns {object[]} Findings, each with a locator where one is obtainable.
439
538
  */
440
- export function lintNote(note, { schemas, index, vocabulary, systems = DEFAULT_SYSTEM_BLOCKS }) {
539
+ export function lintNote(
540
+ note,
541
+ {
542
+ schemas,
543
+ index,
544
+ vocabulary,
545
+ systems = DEFAULT_SYSTEM_BLOCKS,
546
+ landing = DEFAULT_ADDRESS_SCHEME.landing,
547
+ types = [],
548
+ sections = [],
549
+ },
550
+ ) {
441
551
  const findings = [];
442
552
  const fm = note.fm ?? {};
443
553
  const type = String(fm.type ?? "");
@@ -534,7 +644,15 @@ export function lintNote(note, { schemas, index, vocabulary, systems = DEFAULT_S
534
644
  const entry = vocabulary?.[type];
535
645
  if (entry) {
536
646
  findings.push(...checkDataContainer(note, { type, fields: entry.data ?? [] }));
537
- findings.push(...checkSubType(note, { type, entry }));
647
+ findings.push(
648
+ ...checkSubType(note, {
649
+ type,
650
+ entry,
651
+ asSection: subTypeIsSection(note, landing),
652
+ types,
653
+ sections,
654
+ }),
655
+ );
538
656
  }
539
657
 
540
658
  const fields = authoredFields(schema);
@@ -691,10 +809,19 @@ export function lintNote(note, { schemas, index, vocabulary, systems = DEFAULT_S
691
809
  * @param {boolean} [opts.references=true] - Whether to check references.
692
810
  * @param {Readonly<Record<string, {known?: readonly string[], fieldVocabulary?: boolean}>>} [opts.systems]
693
811
  * The system blocks to check. See {@link DEFAULT_SYSTEM_BLOCKS}.
812
+ * @param {string} [opts.landing] - The repository's landing rule; see
813
+ * {@link lintNote}.
814
+ * @param {readonly string[]} [opts.types] - The content types the format
815
+ * declares; see {@link lintNote}.
816
+ * @param {readonly string[]} [opts.sections] - The sections the repository
817
+ * configures; see {@link lintNote}.
694
818
  * @returns {{findings: object[], notes: number}} The findings, and how many
695
819
  * notes were inspected.
696
820
  */
697
- export function lintFrontmatter(index, { schemas, vocabulary, references = true, systems }) {
821
+ export function lintFrontmatter(
822
+ index,
823
+ { schemas, vocabulary, references = true, systems, landing, types, sections },
824
+ ) {
698
825
  const findings = [];
699
826
  const notes = [...index.notes].sort((a, b) =>
700
827
  a.file < b.file ? -1
@@ -708,6 +835,9 @@ export function lintFrontmatter(index, { schemas, vocabulary, references = true,
708
835
  vocabulary,
709
836
  index: references ? index : undefined,
710
837
  ...(systems ? { systems } : {}),
838
+ ...(landing ? { landing } : {}),
839
+ ...(types ? { types } : {}),
840
+ ...(sections ? { sections } : {}),
711
841
  }),
712
842
  );
713
843
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heroiclands/package-build",
3
- "version": "11.0.0",
3
+ "version": "11.1.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",
@@ -14,6 +14,30 @@ export function publishesContentPages(config: {
14
14
  site: SiteMode;
15
15
  };
16
16
  }): boolean;
17
+ /**
18
+ * Every URL section this repository names, in declaration order (#197).
19
+ *
20
+ * A section is *declared* by describing it: `site.sections` for one whose
21
+ * landing this build generates, `site.readmeSections` for one whose landing is
22
+ * a `README`. Between them they are the open set of addresses a repository
23
+ * says it publishes under — which is what a `README` landing's `subType` names,
24
+ * since `sectionOf` reads that field as the section rather than as a genre.
25
+ *
26
+ * Read from the same two maps the site build renders each landing from, so the
27
+ * set a note is checked against and the set a landing is written from cannot
28
+ * come to disagree. A repository that describes no section declares none, and
29
+ * the answer is empty rather than a guess assembled from the tree.
30
+ *
31
+ * @param {{site: {sections: object, readmeSections: object}}} config - A
32
+ * resolved configuration.
33
+ * @returns {readonly string[]} The section names, deduplicated.
34
+ */
35
+ export function declaredSections(config: {
36
+ site: {
37
+ sections: object;
38
+ readmeSections: object;
39
+ };
40
+ }): readonly string[];
17
41
  /**
18
42
  * Validate and normalize a content configuration.
19
43
  *
@@ -29,9 +29,22 @@ export function matchesKind(value: unknown, kind: string): boolean;
29
29
  * @param {Readonly<Record<string, {known?: readonly string[], fieldVocabulary?: boolean}>>} [opts.systems]
30
30
  * The system blocks to check, and what each accepts. See
31
31
  * {@link DEFAULT_SYSTEM_BLOCKS}.
32
+ * @param {string} [opts.landing] - The repository's landing rule, from
33
+ * `publish.address.landing`. It decides which note addresses a whole section,
34
+ * and so whether a `subType` is a genre or an address (#197).
35
+ * @param {readonly string[]} [opts.types] - The content types the format
36
+ * declares — the sections that exist by construction (#200). Read from
37
+ * `docs/content-format.md` rather than from `schemas`, because the two answer
38
+ * different questions: whether an address is real, and whether this build can
39
+ * check a note's fields. A type the specification declares and no schema
40
+ * covers is a real section, and its notes are reported on their own account.
41
+ * @param {readonly string[]} [opts.sections] - The sections the repository
42
+ * configures, from `declaredSections`. Supplied by the caller for the same
43
+ * reason `vocabulary` is: this module checks a note against what it is
44
+ * handed.
32
45
  * @returns {object[]} Findings, each with a locator where one is obtainable.
33
46
  */
34
- export function lintNote(note: object, { schemas, index, vocabulary, systems }: {
47
+ export function lintNote(note: object, { schemas, index, vocabulary, systems, landing, types, sections, }: {
35
48
  schemas: Record<string, readonly object[]>;
36
49
  index?: object | undefined;
37
50
  vocabulary?: Record<string, object> | undefined;
@@ -39,6 +52,9 @@ export function lintNote(note: object, { schemas, index, vocabulary, systems }:
39
52
  known?: readonly string[];
40
53
  fieldVocabulary?: boolean;
41
54
  }>> | undefined;
55
+ landing?: string | undefined;
56
+ types?: readonly string[] | undefined;
57
+ sections?: readonly string[] | undefined;
42
58
  }): object[];
43
59
  /**
44
60
  * Check every note in a built index against its type's schema.
@@ -51,10 +67,16 @@ export function lintNote(note: object, { schemas, index, vocabulary, systems }:
51
67
  * @param {boolean} [opts.references=true] - Whether to check references.
52
68
  * @param {Readonly<Record<string, {known?: readonly string[], fieldVocabulary?: boolean}>>} [opts.systems]
53
69
  * The system blocks to check. See {@link DEFAULT_SYSTEM_BLOCKS}.
70
+ * @param {string} [opts.landing] - The repository's landing rule; see
71
+ * {@link lintNote}.
72
+ * @param {readonly string[]} [opts.types] - The content types the format
73
+ * declares; see {@link lintNote}.
74
+ * @param {readonly string[]} [opts.sections] - The sections the repository
75
+ * configures; see {@link lintNote}.
54
76
  * @returns {{findings: object[], notes: number}} The findings, and how many
55
77
  * notes were inspected.
56
78
  */
57
- export function lintFrontmatter(index: object, { schemas, vocabulary, references, systems }: {
79
+ export function lintFrontmatter(index: object, { schemas, vocabulary, references, systems, landing, types, sections }: {
58
80
  schemas: Record<string, readonly object[]>;
59
81
  vocabulary?: Record<string, object> | undefined;
60
82
  references?: boolean | undefined;
@@ -62,6 +84,9 @@ export function lintFrontmatter(index: object, { schemas, vocabulary, references
62
84
  known?: readonly string[];
63
85
  fieldVocabulary?: boolean;
64
86
  }>> | undefined;
87
+ landing?: string | undefined;
88
+ types?: readonly string[] | undefined;
89
+ sections?: readonly string[] | undefined;
65
90
  }): {
66
91
  findings: object[];
67
92
  notes: number;