@heroiclands/package-build 25.1.3 → 25.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 +29 -0
- package/assets/styles/content-base.css +0 -9
- package/bin/content-commands.mjs +22 -16
- package/bin/package-build.mjs +24 -25
- package/content-config.mjs +7 -34
- package/docs/api.md +38 -21
- package/docs/authoring/first-note.md +3 -3
- package/docs/authoring/frontmatter.md +30 -2
- package/docs/authoring/links-and-markup.md +92 -31
- package/docs/commands.md +103 -59
- package/docs/configuration.md +10 -45
- package/docs/content-format.md +7 -5
- package/docs/diagnostics.md +75 -0
- package/docs/guides/book.md +3 -3
- package/docs/guides/module-project.md +1 -1
- package/docs/guides/reference-pages.md +7 -3
- package/docs/project-setup.md +3 -0
- package/docs/reference/format-details.md +299 -185
- package/docs/reference/note-types.md +16 -17
- package/engine/changelog-group.mjs +84 -173
- package/engine/changelog-labels.mjs +73 -0
- package/engine/changelog-lint.mjs +168 -44
- package/engine/content-expand.mjs +58 -38
- package/engine/content-format.yaml +0 -2
- package/engine/content-index.mjs +49 -23
- package/engine/content-links.mjs +4 -0
- package/engine/content-lint.mjs +2 -3
- package/engine/content-render.mjs +6 -2
- package/engine/culture-parents.mjs +1 -1
- package/engine/culture-reach.mjs +10 -1
- package/engine/diagnostics.mjs +3 -2
- package/engine/dictionary-rows.mjs +3 -1
- package/engine/first-use.mjs +301 -87
- package/engine/frontmatter-lint.mjs +40 -14
- package/engine/glossary-entries.mjs +5 -5
- package/engine/glossary-language.mjs +21 -44
- package/engine/homepage.mjs +0 -4
- package/engine/index-records.mjs +3 -1
- package/engine/infobox.mjs +6 -3
- package/engine/language-names.mjs +3 -2
- package/engine/language-rows.mjs +6 -7
- package/engine/lexical-sections.mjs +3 -1
- package/engine/literature-notes.mjs +4 -76
- package/engine/metadata-index.mjs +15 -8
- package/engine/name-forms.mjs +99 -0
- package/engine/name-refs.mjs +143 -0
- package/engine/name-rows.mjs +11 -1
- package/engine/names.mjs +27 -22
- package/engine/note-addresses.mjs +0 -3
- package/engine/note-frontmatter.mjs +3 -5
- package/engine/note-language.mjs +348 -0
- package/engine/note-vocabulary.mjs +13 -11
- package/engine/pdf-index.mjs +3 -2
- package/engine/pdf-render.mjs +2 -4
- package/engine/settlement-tables.mjs +4 -3
- package/engine/site-build.mjs +2 -7
- package/engine/site-search.mjs +3 -5
- package/engine/site-theme.mjs +1 -1
- package/engine/sql-tables.mjs +29 -14
- package/engine/wikilink-syntax.mjs +2 -0
- package/engine/word-citations.mjs +3 -3
- package/engine/word-rows.mjs +3 -1
- package/engine/word-terms.mjs +2 -2
- package/package.json +1 -1
- package/site-theme/static/css/style.css +0 -4
- package/types/engine/changelog-group.d.mts +6 -10
- package/types/engine/changelog-labels.d.mts +10 -0
- package/types/engine/changelog-lint.d.mts +11 -6
- package/types/engine/content-index.d.mts +16 -16
- package/types/engine/diagnostics.d.mts +1 -1
- package/types/engine/first-use.d.mts +97 -1
- package/types/engine/frontmatter-lint.d.mts +9 -2
- package/types/engine/glossary-language.d.mts +13 -6
- package/types/engine/homepage.d.mts +0 -3
- package/types/engine/literature-notes.d.mts +2 -27
- package/types/engine/name-forms.d.mts +61 -0
- package/types/engine/name-refs.d.mts +51 -0
- package/types/engine/name-rows.d.mts +4 -0
- package/types/engine/names.d.mts +12 -92
- package/types/engine/note-frontmatter.d.mts +1 -4
- package/types/engine/note-language.d.mts +47 -0
- package/types/engine/settlement-tables.d.mts +1 -1
- package/types/engine/site-build.d.mts +2 -6
|
@@ -43,6 +43,7 @@ import { MARKET_CLASSES, marketClass } from "./market-class.mjs";
|
|
|
43
43
|
import { hasTag } from "./note-vocabulary.mjs";
|
|
44
44
|
import { shortcodesOf } from "./holdings.mjs";
|
|
45
45
|
import { WIKILINK, parseWikilink } from "./wikilink-syntax.mjs";
|
|
46
|
+
import { nameFormsOf } from "./name-forms.mjs";
|
|
46
47
|
|
|
47
48
|
/** The heading text a settlements table stands under, at level two. */
|
|
48
49
|
export const SETTLEMENTS_HEADING = "Settlements";
|
|
@@ -332,7 +333,7 @@ export function findSettlementTables(raw) {
|
|
|
332
333
|
* @property {string} shortcode - Lower case.
|
|
333
334
|
* @property {string} subType
|
|
334
335
|
* @property {string} title
|
|
335
|
-
* @property {Set<string>} names - Its name and
|
|
336
|
+
* @property {Set<string>} names - Its name and listed forms, lower case.
|
|
336
337
|
* @property {string[]} parents - Enclosing places' shortcodes.
|
|
337
338
|
* @property {{heading: number, tables: SettlementTable[]}} section
|
|
338
339
|
*/
|
|
@@ -353,7 +354,7 @@ export function findSettlementTables(raw) {
|
|
|
353
354
|
const CORPUS = new WeakMap();
|
|
354
355
|
|
|
355
356
|
/**
|
|
356
|
-
* A note's name and
|
|
357
|
+
* A note's name and listed forms, lower case.
|
|
357
358
|
*
|
|
358
359
|
* @param {object} fm - The frontmatter.
|
|
359
360
|
* @returns {Set<string>} The names.
|
|
@@ -367,7 +368,7 @@ function namesOf(fm) {
|
|
|
367
368
|
if (typeof name === "string") add(name);
|
|
368
369
|
else if (name && typeof name === "object") {
|
|
369
370
|
add(name.full);
|
|
370
|
-
for (const
|
|
371
|
+
for (const form of nameFormsOf(name)) add(form);
|
|
371
372
|
}
|
|
372
373
|
return out;
|
|
373
374
|
}
|
package/engine/site-build.mjs
CHANGED
|
@@ -578,9 +578,6 @@ export function tableUniverse(pages) {
|
|
|
578
578
|
/**
|
|
579
579
|
* The frontmatter a page publishes with.
|
|
580
580
|
*
|
|
581
|
-
* An authored `aliases` is a list of names a reader might call the note. It is
|
|
582
|
-
* dropped: this build emits no redirects.
|
|
583
|
-
*
|
|
584
581
|
* A content page carries the package the build **derived**. No note
|
|
585
582
|
* declares one, so the note's frontmatter alone would publish a page that does
|
|
586
583
|
* not say which package it belongs to. Writing the derived value keeps a page
|
|
@@ -589,9 +586,8 @@ export function tableUniverse(pages) {
|
|
|
589
586
|
*
|
|
590
587
|
* **`related` is derived, never authored.** What links to a page is a fact
|
|
591
588
|
* about every other page, known only once the whole tree has resolved — see
|
|
592
|
-
* {@link module:engine/related-pages} — so an authored value is dropped
|
|
593
|
-
*
|
|
594
|
-
* holds the graph.
|
|
589
|
+
* {@link module:engine/related-pages} — so an authored value is dropped, and
|
|
590
|
+
* {@link renderPages} sets the derived block once it holds the graph.
|
|
595
591
|
*
|
|
596
592
|
* @param {object} page - The page.
|
|
597
593
|
* @param {object} options
|
|
@@ -621,7 +617,6 @@ export function pageFrontmatter(page, { decorate, webSrc, artSrc }) {
|
|
|
621
617
|
kbfolder: page.folder,
|
|
622
618
|
};
|
|
623
619
|
if (decorate) decorate(data, page);
|
|
624
|
-
delete data.aliases;
|
|
625
620
|
delete data.related;
|
|
626
621
|
if (webSrc && artSrc) resolveArtFields(data, webSrc, artSrc);
|
|
627
622
|
return data;
|
package/engine/site-search.mjs
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* the world. A note reached by more than one path carries every continent and
|
|
31
31
|
* region it reaches, so a being with homes in two regions is found under both.
|
|
32
32
|
*
|
|
33
|
-
* The note's name and its
|
|
33
|
+
* The note's name and its listed forms are weighted, so a search for a name finds
|
|
34
34
|
* its note first. A note's tags and its draft state are not filters.
|
|
35
35
|
*
|
|
36
36
|
* @module
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
import { placeShortcodes } from "./map-places.mjs";
|
|
40
40
|
import { resolveInfoboxRef } from "./site-index.mjs";
|
|
41
|
+
import { nameFormsOf } from "./name-forms.mjs";
|
|
41
42
|
|
|
42
43
|
/** The filters a note's page carries, in the order a reader is offered them. */
|
|
43
44
|
export const SEARCH_FILTERS = Object.freeze([
|
|
@@ -153,9 +154,6 @@ export function searchMetadata(fm, { geography, index }) {
|
|
|
153
154
|
values: values[name].filter(Boolean),
|
|
154
155
|
})).filter((filter) => filter.values.length);
|
|
155
156
|
const name = isMapping(fm?.name) ? fm.name : {};
|
|
156
|
-
const names = [
|
|
157
|
-
text(name.full),
|
|
158
|
-
...(Array.isArray(name.aliases) ? name.aliases.map(text) : []),
|
|
159
|
-
].filter(Boolean);
|
|
157
|
+
const names = [text(name.full), ...nameFormsOf(name).map(text)].filter(Boolean);
|
|
160
158
|
return { filters, names: [...new Set(names)] };
|
|
161
159
|
}
|
package/engine/site-theme.mjs
CHANGED
|
@@ -261,7 +261,7 @@ export function tableOfContents(headings) {
|
|
|
261
261
|
return lines.join("\n");
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
/** How much more a page's name and
|
|
264
|
+
/** How much more a page's name and listed forms count in a search than a word of its body. */
|
|
265
265
|
const NAME_WEIGHT = 10;
|
|
266
266
|
|
|
267
267
|
/** The most `h2` headings a page may have before it gets a contents list. */
|
package/engine/sql-tables.mjs
CHANGED
|
@@ -55,6 +55,7 @@ import { LANGUAGE_COLUMN_TYPES, LANGUAGE_RELATIONS } from "./language-rows.mjs";
|
|
|
55
55
|
import { VERB_PERSON_FIELDS } from "./language-terms.mjs";
|
|
56
56
|
import { renderDictionary, renderGlossary } from "./dictionary-layout.mjs";
|
|
57
57
|
import { glossaryLanguage } from "./glossary-language.mjs";
|
|
58
|
+
import { languageNotesByCode } from "./note-language.mjs";
|
|
58
59
|
import { lexicalSectionsFor } from "./lexical-sections.mjs";
|
|
59
60
|
import { eventViewSections } from "./event-views.mjs";
|
|
60
61
|
import { appendGeneratedSections, joinSections } from "./generated-sections.mjs";
|
|
@@ -466,6 +467,22 @@ export async function openNotesDatabase(
|
|
|
466
467
|
system: "note",
|
|
467
468
|
},
|
|
468
469
|
stubsExcluded,
|
|
470
|
+
// Each language code's note, for a page whose language is read from
|
|
471
|
+
// its frontmatter.
|
|
472
|
+
languageNotes: languageNotesByCode(encoded, (record) =>
|
|
473
|
+
(
|
|
474
|
+
typeof record.package === "string" &&
|
|
475
|
+
typeof record.type === "string" &&
|
|
476
|
+
typeof record.shortcode === "string"
|
|
477
|
+
) ?
|
|
478
|
+
renderAddress({
|
|
479
|
+
package: record.package,
|
|
480
|
+
system: "note",
|
|
481
|
+
type: record.type,
|
|
482
|
+
shortcode: record.shortcode,
|
|
483
|
+
})
|
|
484
|
+
: undefined,
|
|
485
|
+
),
|
|
469
486
|
async query(sql) {
|
|
470
487
|
const reader = await connection.runAndReadAll(sql);
|
|
471
488
|
return {
|
|
@@ -1019,7 +1036,7 @@ function toPlain(value) {
|
|
|
1019
1036
|
* DuckDB returns an integer as a `BigInt`, which `String()` renders without its
|
|
1020
1037
|
* `n` — but a struct or a list would stringify as `[object Object]`, so those
|
|
1021
1038
|
* are refused rather than printed. A list of scalars is joined, because that is
|
|
1022
|
-
* what a `tags`
|
|
1039
|
+
* what a `tags` column means.
|
|
1023
1040
|
*
|
|
1024
1041
|
* @param {unknown} value - The cell value.
|
|
1025
1042
|
* @param {string} column - Its column, named in the message.
|
|
@@ -1296,20 +1313,17 @@ async function noteLabels(db) {
|
|
|
1296
1313
|
|
|
1297
1314
|
/**
|
|
1298
1315
|
* The language a note's frontmatter says it is in: a language note's own
|
|
1299
|
-
* Address, else its
|
|
1316
|
+
* Address, else the note of its language — the code the content index derived
|
|
1317
|
+
* for it where the frontmatter is its record, else its own `data.lang`.
|
|
1300
1318
|
*
|
|
1301
|
-
* @param {object|undefined} frontmatter - The note's
|
|
1302
|
-
* @param {object} db - From {@link openNotesDatabase}, for its Address context
|
|
1319
|
+
* @param {object|undefined} frontmatter - The note's frontmatter or record.
|
|
1320
|
+
* @param {object} db - From {@link openNotesDatabase}, for its Address context
|
|
1321
|
+
* and its language notes.
|
|
1303
1322
|
* @returns {string|undefined} The language's Address.
|
|
1304
1323
|
*/
|
|
1305
1324
|
function frontmatterLanguage(frontmatter, db) {
|
|
1306
1325
|
if (!frontmatter || typeof frontmatter !== "object") return undefined;
|
|
1307
1326
|
const context = db.addressContext ?? {};
|
|
1308
|
-
const read = (value, type) => {
|
|
1309
|
-
if (typeof value !== "string" || !value) return undefined;
|
|
1310
|
-
const tuple = parseAddress(value, { ...context, system: "note", type }, { declared: true });
|
|
1311
|
-
return tuple.reason ? undefined : renderAddress({ ...tuple, system: "note" });
|
|
1312
|
-
};
|
|
1313
1327
|
if (
|
|
1314
1328
|
String(frontmatter.type ?? "").toLowerCase() === "skill" &&
|
|
1315
1329
|
frontmatter.subType === "language" &&
|
|
@@ -1322,7 +1336,8 @@ function frontmatterLanguage(frontmatter, db) {
|
|
|
1322
1336
|
type: "skill",
|
|
1323
1337
|
shortcode: frontmatter.shortcode,
|
|
1324
1338
|
});
|
|
1325
|
-
|
|
1339
|
+
const code = frontmatter.lang ?? frontmatter.data?.lang;
|
|
1340
|
+
return typeof code === "string" ? db.languageNotes?.get(code) : undefined;
|
|
1326
1341
|
}
|
|
1327
1342
|
|
|
1328
1343
|
/** Evaluate scalar SQL calls before synchronous Markdown renderers run. */
|
|
@@ -1535,9 +1550,8 @@ async function attachPreparedAnchors(prepared, records, contentBase, parsed, con
|
|
|
1535
1550
|
|
|
1536
1551
|
/**
|
|
1537
1552
|
* The language each note of a tree is in, by its file: a language note itself,
|
|
1538
|
-
*
|
|
1539
|
-
*
|
|
1540
|
-
* {@link module:engine/glossary-language}.
|
|
1553
|
+
* and every other note the note of the language the content index derived for
|
|
1554
|
+
* it — see {@link module:engine/glossary-language}.
|
|
1541
1555
|
*
|
|
1542
1556
|
* @param {object[]} records - Content-index records.
|
|
1543
1557
|
* @param {string} contentBase - Root of the content tree.
|
|
@@ -1566,12 +1580,13 @@ function pageLanguages(records, contentBase) {
|
|
|
1566
1580
|
.filter((record) => isNoteRecord(record))
|
|
1567
1581
|
.map((record, i) => [noteFile(contentBase, record), notes[i]]),
|
|
1568
1582
|
);
|
|
1583
|
+
const languageNotes = languageNotesByCode(byKey.values(), keyOf);
|
|
1569
1584
|
return (source) => {
|
|
1570
1585
|
const record = byFile.get(source);
|
|
1571
1586
|
if (!record) return undefined;
|
|
1572
1587
|
return glossaryLanguage(undefined, record, {
|
|
1573
1588
|
keyOf: (value) => (typeof value === "string" && value ? value : undefined),
|
|
1574
|
-
|
|
1589
|
+
noteOf: (code) => languageNotes.get(code),
|
|
1575
1590
|
self: keyOf(record),
|
|
1576
1591
|
});
|
|
1577
1592
|
};
|
|
@@ -104,6 +104,8 @@ export function parseWikilink(rawInner, literal = "") {
|
|
|
104
104
|
const attributes =
|
|
105
105
|
suffix.startsWith("{") ? parseExtensionAttributes(suffix.slice(1, -1)) : null;
|
|
106
106
|
if (attributes) attributes.problems.push(...refusedAttributes(attributes.values));
|
|
107
|
+
if (attributes && Object.hasOwn(attributes.values, "ref"))
|
|
108
|
+
attributes.problems.push("a wikilink never takes ref; its address is its ref");
|
|
107
109
|
return {
|
|
108
110
|
inner: inner.trim(),
|
|
109
111
|
target,
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
import MarkdownIt from "markdown-it";
|
|
41
41
|
|
|
42
42
|
import { isLanguageRecord, noteAddressOf } from "./word-rows.mjs";
|
|
43
|
+
import { nameFormsOf } from "./name-forms.mjs";
|
|
43
44
|
|
|
44
45
|
/** The parser marked uses are read with: CommonMark's emphasis and nothing added. */
|
|
45
46
|
const PARSER = new MarkdownIt("commonmark");
|
|
@@ -101,7 +102,7 @@ export function markedUses(body) {
|
|
|
101
102
|
|
|
102
103
|
/**
|
|
103
104
|
* The names a note carries whole, which cite a word they spell exactly: its
|
|
104
|
-
* full name and
|
|
105
|
+
* full name and listed forms, and an affiliation's rank and office titles.
|
|
105
106
|
*
|
|
106
107
|
* @param {Record<string, any>} record - An index record.
|
|
107
108
|
* @returns {string[]}
|
|
@@ -110,8 +111,7 @@ function namesCarried(record) {
|
|
|
110
111
|
const out = [];
|
|
111
112
|
const name = isMap(record.name) ? record.name : {};
|
|
112
113
|
if (typeof name.full === "string") out.push(name.full);
|
|
113
|
-
|
|
114
|
-
if (typeof alias === "string") out.push(alias);
|
|
114
|
+
out.push(...nameFormsOf(name));
|
|
115
115
|
const governance = isMap(record.data?.governance) ? record.data.governance : {};
|
|
116
116
|
for (const rank of Array.isArray(governance.ranks) ? governance.ranks : [])
|
|
117
117
|
if (isMap(rank) && typeof rank.title === "string") out.push(rank.title);
|
package/engine/word-rows.mjs
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
import { renderAddress } from "./address.mjs";
|
|
38
38
|
import { glossaryLanguage } from "./glossary-language.mjs";
|
|
39
|
+
import { languageNotesByCode } from "./note-language.mjs";
|
|
39
40
|
import {
|
|
40
41
|
AFFIX_KEYS,
|
|
41
42
|
ZERO_AFFIX,
|
|
@@ -368,9 +369,10 @@ export function glossaryRows(records, all = records) {
|
|
|
368
369
|
const key = noteAddressOf(record);
|
|
369
370
|
if (key && !byAddress.has(key)) byAddress.set(key, record);
|
|
370
371
|
}
|
|
372
|
+
const languageNotes = languageNotesByCode(byAddress.values(), noteAddressOf);
|
|
371
373
|
const reader = {
|
|
372
374
|
keyOf: (value) => (typeof value === "string" && value ? value : undefined),
|
|
373
|
-
|
|
375
|
+
noteOf: (code) => languageNotes.get(code),
|
|
374
376
|
};
|
|
375
377
|
const rows = [];
|
|
376
378
|
for (const record of records) {
|
package/engine/word-terms.mjs
CHANGED
|
@@ -489,8 +489,8 @@ export const GLOSSARY_ENTRY_FIELDS = Object.freeze([
|
|
|
489
489
|
kind: "address",
|
|
490
490
|
shape: "a language skill Address",
|
|
491
491
|
describe:
|
|
492
|
-
"The language the term is in. Unstated, it is the
|
|
493
|
-
"
|
|
492
|
+
"The language the term is in. Unstated, it is the note of the language the " +
|
|
493
|
+
"note is in, its `data.lang` as the content index derives it.",
|
|
494
494
|
}),
|
|
495
495
|
]);
|
|
496
496
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.2.0",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package \u2014 content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `changelog group`:
|
|
3
|
-
* their bold label, order them, and file an undeclared one last.
|
|
2
|
+
* `changelog group`: give the newest release its final shape.
|
|
4
3
|
*
|
|
5
|
-
* Every earlier release section is untouched, byte for byte
|
|
6
|
-
*
|
|
7
|
-
* each independently (a label groups within its own bump level, never
|
|
8
|
-
* across one). Running this on its own output is a no-op: a release already
|
|
9
|
-
* in label order, with each label merged to one block, groups to itself.
|
|
4
|
+
* Every earlier release section is untouched, byte for byte. Running this on
|
|
5
|
+
* its own output is a no-op.
|
|
10
6
|
*
|
|
11
7
|
* @param {string} text - The changelog's full contents.
|
|
12
8
|
* @param {object} [opts]
|
|
13
|
-
* @param {readonly string[]|null} [opts.labels] - `
|
|
14
|
-
* display order. `null`/absent orders
|
|
15
|
-
*
|
|
9
|
+
* @param {readonly string[]|null} [opts.labels] - the changelog `labels`, in
|
|
10
|
+
* display order. `null`/absent orders by first appearance and reports
|
|
11
|
+
* nothing as undeclared.
|
|
16
12
|
* @returns {{text: string, findings: Array<{line: number,
|
|
17
13
|
* severity: "warning", message: string}>}}
|
|
18
14
|
*/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `labels` from the changelog options in `.changeset/config.json`.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} [cwd] - The repository root.
|
|
5
|
+
* @returns {readonly string[]|null} The labels in display order, or `null`
|
|
6
|
+
* when the file, the options or `labels` is absent.
|
|
7
|
+
* @throws {Error} When `config.json` is not JSON, or `labels` is not an array
|
|
8
|
+
* of distinct non-empty strings or names `Miscellaneous`.
|
|
9
|
+
*/
|
|
10
|
+
export function readChangelogLabels(cwd?: string): readonly string[] | null;
|
|
@@ -77,9 +77,9 @@ export function topLevelBlocks(text: string, codeLines: Set<number>, scaffoldLin
|
|
|
77
77
|
*
|
|
78
78
|
* @param {string} text - The file's full contents, frontmatter included.
|
|
79
79
|
* @param {object} [opts]
|
|
80
|
-
* @param {readonly string[]|null} [opts.labels] - `
|
|
81
|
-
* display order, or `null`/absent when the repository declares none
|
|
82
|
-
* {@link
|
|
80
|
+
* @param {readonly string[]|null} [opts.labels] - the changelog `labels`, in
|
|
81
|
+
* display order, or `null`/absent when the repository declares none, in
|
|
82
|
+
* which case only {@link MISCELLANEOUS} is accepted.
|
|
83
83
|
* @returns {{findings: Array<{line: number, column?: number,
|
|
84
84
|
* severity: "error"|"warning", message: string}>}}
|
|
85
85
|
*/
|
|
@@ -98,9 +98,9 @@ export function lintChangesetText(text: string, { labels }?: {
|
|
|
98
98
|
*
|
|
99
99
|
* @param {string} text - The changelog's full contents.
|
|
100
100
|
* @param {object} [opts]
|
|
101
|
-
* @param {readonly string[]|null} [opts.labels] - `
|
|
102
|
-
* display order, or `null`/absent when the repository declares none
|
|
103
|
-
* {@link
|
|
101
|
+
* @param {readonly string[]|null} [opts.labels] - the changelog `labels`, in
|
|
102
|
+
* display order, or `null`/absent when the repository declares none, in
|
|
103
|
+
* which case only {@link MISCELLANEOUS} is accepted.
|
|
104
104
|
* @returns {{findings: Array<{line?: number, column?: number,
|
|
105
105
|
* severity: "error"|"warning", message: string}>}}
|
|
106
106
|
*/
|
|
@@ -135,6 +135,11 @@ export function releaseSectionRange(text: string): {
|
|
|
135
135
|
start: number;
|
|
136
136
|
end: number;
|
|
137
137
|
} | null;
|
|
138
|
+
/**
|
|
139
|
+
* The label every repository may use without declaring it, for the changes
|
|
140
|
+
* that fit no declared label. `changelog group` writes it last.
|
|
141
|
+
*/
|
|
142
|
+
export const MISCELLANEOUS: "Miscellaneous";
|
|
138
143
|
/**
|
|
139
144
|
* The finding one rule reports, before its line is mapped into the caller's
|
|
140
145
|
* file.
|
|
@@ -32,30 +32,30 @@ export function noteAddress(frontmatter: Record<string, any>, contentPackage: st
|
|
|
32
32
|
canonical: string;
|
|
33
33
|
} | null;
|
|
34
34
|
/**
|
|
35
|
-
* A note's
|
|
35
|
+
* A note's listed name forms reduced to printable 7-bit ASCII, by language.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* A listed form is a name a reader is at least as likely to reach for as the
|
|
38
|
+
* full one — `Killer Whale` for an orca, a people's own name for the people —
|
|
39
|
+
* so anything searching or completing over the index has to match them too,
|
|
40
|
+
* and needs the same keyboard-typeable form {@link asciiName} gives the full
|
|
41
|
+
* name.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
* came from.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
43
|
+
* Each language keeps its authored order, so a caller can pair an entry with
|
|
44
|
+
* the form it came from. A form that leaves nothing printable behind is
|
|
45
|
+
* dropped rather than left as a hole, and a language left with no form is
|
|
46
|
+
* left out.
|
|
47
47
|
*
|
|
48
|
-
* @param {unknown}
|
|
49
|
-
* @returns {
|
|
50
|
-
* has an empty
|
|
51
|
-
*
|
|
48
|
+
* @param {unknown} name - The note's `name`; may be absent or null.
|
|
49
|
+
* @returns {Record<string, string[]>} Possibly empty, never null: a note
|
|
50
|
+
* listing no form has an empty map, which a consumer iterating it need not
|
|
51
|
+
* check first.
|
|
52
52
|
*/
|
|
53
|
-
export function
|
|
53
|
+
export function asciiForms(name: unknown): Record<string, string[]>;
|
|
54
54
|
/**
|
|
55
55
|
* Build one index record from a note's frontmatter and its place in the tree.
|
|
56
56
|
*
|
|
57
57
|
* The frontmatter as authored, plus what the index derives from it: the package
|
|
58
|
-
* it compiles as, its address, ASCII folds of its name and
|
|
58
|
+
* it compiles as, its address, ASCII folds of its name and listed forms, the anchors
|
|
59
59
|
* of its body, its Foundry block, the address of its documentation journal, and
|
|
60
60
|
* where the file sits within the tree.
|
|
61
61
|
*
|
|
@@ -130,7 +130,7 @@ export function positionInBody(body: string, offset: number, { bodyLine, bodyCol
|
|
|
130
130
|
* @param {string} raw - The file's full contents, frontmatter included.
|
|
131
131
|
* @param {string} key - The top-level frontmatter key.
|
|
132
132
|
* @param {string} [value] - When given, prefer the occurrence whose line also
|
|
133
|
-
* carries this text. A list-valued key (`
|
|
133
|
+
* carries this text. A list-valued key (`tags`) is reported at the entry
|
|
134
134
|
* that is wrong, not at the key that introduces it.
|
|
135
135
|
* @param {object} [options] - Options.
|
|
136
136
|
* @param {boolean} [options.topLevel=false] - Require the key at column 1, so
|
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A use of a name in a page's prose.
|
|
3
|
+
*
|
|
4
|
+
* @typedef {object} NameUse
|
|
5
|
+
* @property {number} start - Inclusive UTF-16 offset of the use.
|
|
6
|
+
* @property {number} end - Exclusive UTF-16 offset after it, attributes included.
|
|
7
|
+
* @property {string} key - What the use is counted under.
|
|
8
|
+
* @property {boolean} emphasised - It sits inside authored strong text.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Every use of a name in a body's prose, in source order.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} markdown - A body, as authored or expanded.
|
|
14
|
+
* @param {NameVocabulary} [vocabulary] - What its addresses are read against.
|
|
15
|
+
* @returns {NameUse[]} The uses that count toward a first use.
|
|
16
|
+
*/
|
|
17
|
+
export function nameUses(markdown: string, vocabulary?: NameVocabulary): NameUse[];
|
|
18
|
+
/**
|
|
19
|
+
* Mark the first use of each name in a body's prose as bold.
|
|
20
|
+
*
|
|
21
|
+
* Each first use is wrapped in {@link FIRST_USE_OPEN} and
|
|
22
|
+
* {@link FIRST_USE_CLOSE}, which {@link firstUsePlugin} renders as strong
|
|
23
|
+
* text. A first use already inside authored strong text is left as it is.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} markdown - A body's prose, with its generated sections split
|
|
26
|
+
* off and its wikilinks not yet resolved.
|
|
27
|
+
* @param {NameVocabulary} [vocabulary] - What its addresses are read against.
|
|
28
|
+
* @returns {string} The same markdown with each first use marked.
|
|
29
|
+
*/
|
|
30
|
+
export function boldFirstUses(markdown: string, vocabulary?: NameVocabulary): string;
|
|
31
|
+
/**
|
|
32
|
+
* Render the first-use marks {@link boldFirstUses} wrote as strong text.
|
|
33
|
+
*
|
|
34
|
+
* A mark in inline text opens or closes a `strong`; a mark anywhere else, as in
|
|
35
|
+
* raw HTML or code, is dropped, so no mark reaches a reader.
|
|
36
|
+
*
|
|
37
|
+
* @param {import("markdown-it")} md - Markdown parser to extend.
|
|
38
|
+
*/
|
|
39
|
+
export function firstUsePlugin(md: typeof import("markdown-it")): void;
|
|
40
|
+
/**
|
|
41
|
+
* Prepare indexed names once, then check each note's marked names without
|
|
42
|
+
* reading files.
|
|
43
|
+
*
|
|
44
|
+
* One rule, a warning at the occurrence:
|
|
45
|
+
*
|
|
46
|
+
* - `unlinked-name`: a `.name` span with no `ref` names an indexed note, which
|
|
47
|
+
* a link or a `ref` ties to its note.
|
|
48
|
+
*
|
|
3
49
|
* @param {{records: readonly object[], index: object,
|
|
4
50
|
* catalog?: import("./names.mjs").NameCatalog,
|
|
5
51
|
* parsed?: Map<string, {raw: string, body: string, bodyLine?: number, bodyColumn?: number}>,
|
|
@@ -29,3 +75,53 @@ export function createFirstUseChecker({ records, index, parsed, syntax, catalog,
|
|
|
29
75
|
rules: string[];
|
|
30
76
|
message: string;
|
|
31
77
|
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Opens a first use in expanded markdown: a Unicode noncharacter, which no
|
|
80
|
+
* authored text, font or encoding uses.
|
|
81
|
+
*/
|
|
82
|
+
export const FIRST_USE_OPEN: "\uFDD0";
|
|
83
|
+
/** Closes a first use in expanded markdown. */
|
|
84
|
+
export const FIRST_USE_CLOSE: "\uFDD1";
|
|
85
|
+
/**
|
|
86
|
+
* The addresses a page's names are read against: the tree's types and
|
|
87
|
+
* packages, and the package an address that names none belongs to.
|
|
88
|
+
*/
|
|
89
|
+
export type NameVocabulary = {
|
|
90
|
+
/**
|
|
91
|
+
* - Every type an address may name.
|
|
92
|
+
*/
|
|
93
|
+
types?: Set<string> | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* - Every package an address may name.
|
|
96
|
+
*/
|
|
97
|
+
packages?: Set<string> | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* - Packages with no content index.
|
|
100
|
+
*/
|
|
101
|
+
noIndexPackages?: Set<string> | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* - The package the page belongs to.
|
|
104
|
+
*/
|
|
105
|
+
contentPackage?: string | undefined;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* A use of a name in a page's prose.
|
|
109
|
+
*/
|
|
110
|
+
export type NameUse = {
|
|
111
|
+
/**
|
|
112
|
+
* - Inclusive UTF-16 offset of the use.
|
|
113
|
+
*/
|
|
114
|
+
start: number;
|
|
115
|
+
/**
|
|
116
|
+
* - Exclusive UTF-16 offset after it, attributes included.
|
|
117
|
+
*/
|
|
118
|
+
end: number;
|
|
119
|
+
/**
|
|
120
|
+
* - What the use is counted under.
|
|
121
|
+
*/
|
|
122
|
+
key: string;
|
|
123
|
+
/**
|
|
124
|
+
* - It sits inside authored strong text.
|
|
125
|
+
*/
|
|
126
|
+
emphasised: boolean;
|
|
127
|
+
};
|
|
@@ -54,13 +54,16 @@ export function systemAddressFindings(note: object, { index, schemas, systems }?
|
|
|
54
54
|
* configuration through {@link systemBlocksFor}, and this module states no
|
|
55
55
|
* system name of its own. See {@link DEFAULT_SYSTEM_BLOCKS} for the fallback.
|
|
56
56
|
* @param {object} [opts.addressContext] - Builder package and Address type vocabulary.
|
|
57
|
+
* @param {Iterable<string>} [opts.languages] - The language codes the
|
|
58
|
+
* configuration's `languages:` declares. A `name` key that is one of them, or
|
|
59
|
+
* `en`, is a list of forms; absent, only `en` is.
|
|
57
60
|
* @param {readonly string[]} [opts.packs] - The pack names this package
|
|
58
61
|
* declares, for a `data:` field whose map is keyed by pack. Supplied by the
|
|
59
62
|
* caller like `schemas` and `vocabulary`, and absent it no claim is made
|
|
60
63
|
* about those keys.
|
|
61
64
|
* @returns {object[]} Findings, each with a locator where one is obtainable.
|
|
62
65
|
*/
|
|
63
|
-
export function lintNote(note: object, { schemas, index, currentDate, vocabulary, packs, emittedArt, addressContext, systems, }: {
|
|
66
|
+
export function lintNote(note: object, { schemas, index, currentDate, vocabulary, packs, emittedArt, addressContext, languages, systems, }: {
|
|
64
67
|
schemas: Record<string, readonly object[]>;
|
|
65
68
|
currentDate?: string | undefined;
|
|
66
69
|
index?: object | undefined;
|
|
@@ -71,6 +74,7 @@ export function lintNote(note: object, { schemas, index, currentDate, vocabulary
|
|
|
71
74
|
} | null) | undefined;
|
|
72
75
|
systems?: Readonly<Record<string, SystemBlockSpec>> | undefined;
|
|
73
76
|
addressContext?: object | undefined;
|
|
77
|
+
languages?: Iterable<string> | undefined;
|
|
74
78
|
packs?: readonly string[] | undefined;
|
|
75
79
|
}): object[];
|
|
76
80
|
/**
|
|
@@ -86,6 +90,8 @@ export function lintNote(note: object, { schemas, index, currentDate, vocabulary
|
|
|
86
90
|
* @param {Readonly<Record<string, SystemBlockSpec>>} [opts.systems]
|
|
87
91
|
* The system blocks to check; see {@link lintNote} and
|
|
88
92
|
* {@link systemBlocksFor}.
|
|
93
|
+
* @param {Iterable<string>} [opts.languages] - The configured language codes,
|
|
94
|
+
* which a note's `name` lists its forms under; see {@link lintNote}.
|
|
89
95
|
* @param {readonly string[]} [opts.packs] - The declared pack names; see
|
|
90
96
|
* {@link lintNote}.
|
|
91
97
|
* @param {(type: string) => {document: string|null, art: readonly string[]}|null} [opts.emittedArt]
|
|
@@ -93,12 +99,13 @@ export function lintNote(note: object, { schemas, index, currentDate, vocabulary
|
|
|
93
99
|
* @returns {{findings: object[], notes: number}} The findings, and how many
|
|
94
100
|
* notes were inspected.
|
|
95
101
|
*/
|
|
96
|
-
export function lintFrontmatter(index: object, { schemas, vocabulary, packs, emittedArt, references, systems, currentDate }: {
|
|
102
|
+
export function lintFrontmatter(index: object, { schemas, vocabulary, packs, emittedArt, references, systems, currentDate, languages }: {
|
|
97
103
|
schemas: Record<string, readonly object[]>;
|
|
98
104
|
currentDate?: string | undefined;
|
|
99
105
|
vocabulary?: Record<string, object> | undefined;
|
|
100
106
|
references?: boolean | undefined;
|
|
101
107
|
systems?: Readonly<Record<string, SystemBlockSpec>> | undefined;
|
|
108
|
+
languages?: Iterable<string> | undefined;
|
|
102
109
|
packs?: readonly string[] | undefined;
|
|
103
110
|
emittedArt?: ((type: string) => {
|
|
104
111
|
document: string | null;
|
|
@@ -5,10 +5,17 @@
|
|
|
5
5
|
* @property {(value: unknown, type: string) => string|undefined} keyOf - A
|
|
6
6
|
* value's canonical Address in the `note` system, read with `type` as the
|
|
7
7
|
* default type, or `undefined` where it is no Address.
|
|
8
|
-
* @property {(
|
|
9
|
-
*
|
|
8
|
+
* @property {(code: string) => string|undefined} noteOf - The Address of a
|
|
9
|
+
* language code's note, or `undefined` where no note is configured for it.
|
|
10
10
|
* @property {string} [self] - The owning note's own Address, for a language.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* The language a note is in, as the content index derived it.
|
|
14
|
+
*
|
|
15
|
+
* @param {object} note - An index record, or a link-index note carrying it.
|
|
16
|
+
* @returns {string|undefined} The code.
|
|
17
|
+
*/
|
|
18
|
+
export function noteLang(note: object): string | undefined;
|
|
12
19
|
/**
|
|
13
20
|
* The language a glossary entry resolves to.
|
|
14
21
|
*
|
|
@@ -19,7 +26,7 @@
|
|
|
19
26
|
* @returns {string|undefined} The language's Address, or `undefined` where the
|
|
20
27
|
* chain finds none.
|
|
21
28
|
*/
|
|
22
|
-
export function glossaryLanguage(entry: object | undefined, owner: object, { keyOf,
|
|
29
|
+
export function glossaryLanguage(entry: object | undefined, owner: object, { keyOf, noteOf, self }: GlossaryLanguageReader): string | undefined;
|
|
23
30
|
/**
|
|
24
31
|
* How a caller reads Addresses and finds notes.
|
|
25
32
|
*/
|
|
@@ -31,10 +38,10 @@ export type GlossaryLanguageReader = {
|
|
|
31
38
|
*/
|
|
32
39
|
keyOf: (value: unknown, type: string) => string | undefined;
|
|
33
40
|
/**
|
|
34
|
-
* - The
|
|
35
|
-
*
|
|
41
|
+
* - The Address of a
|
|
42
|
+
* language code's note, or `undefined` where no note is configured for it.
|
|
36
43
|
*/
|
|
37
|
-
|
|
44
|
+
noteOf: (code: string) => string | undefined;
|
|
38
45
|
/**
|
|
39
46
|
* - The owning note's own Address, for a language.
|
|
40
47
|
*/
|
|
@@ -140,9 +140,6 @@ export function homepageTitle(fm: object | null | undefined, config: object): st
|
|
|
140
140
|
* **No `url` and no `slug`.** The homepage publishes at the package root, so
|
|
141
141
|
* the page has no address to state.
|
|
142
142
|
*
|
|
143
|
-
* An authored `aliases` is dropped, as it is on every other page: this build
|
|
144
|
-
* emits no redirects.
|
|
145
|
-
*
|
|
146
143
|
* `related` is derived like every other page's — what links to the homepage
|
|
147
144
|
* is a fact about the rest of the tree — so an authored value is dropped and
|
|
148
145
|
* the caller's block, where it has one, is set in its place.
|