@heroiclands/package-build 23.5.0 → 23.6.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 +33 -0
- package/bin/content-commands.mjs +23 -5
- package/bin/package-build.mjs +1 -74
- package/docs/api.md +9 -0
- package/docs/authoring/links-and-markup.md +77 -12
- package/docs/commands.md +4 -48
- package/docs/content-format.md +16 -0
- package/docs/diagnostics.md +1 -2
- package/docs/guides/module-project.md +1 -1
- package/docs/guides/reference-pages.md +89 -0
- package/docs/guides/release-and-deploy.md +3 -3
- package/docs/project-setup.md +16 -59
- package/docs/reference/format-details.md +533 -84
- package/docs/reference/note-types.md +191 -149
- package/engine/base-compiler.mjs +21 -0
- package/engine/cdn-publish.mjs +30 -5
- package/engine/content-format.yaml +9 -0
- package/engine/content-index.mjs +19 -5
- package/engine/culture-reach.mjs +330 -0
- package/engine/dictionary-layout.mjs +340 -0
- package/engine/dictionary-rows.mjs +608 -0
- package/engine/glossary-entries.mjs +182 -0
- package/engine/glossary-language.mjs +99 -0
- package/engine/index.mjs +1 -0
- package/engine/infobox.mjs +8 -0
- package/engine/language-words.mjs +360 -0
- package/engine/literature-notes.mjs +10 -8
- package/engine/note-addresses.mjs +25 -0
- package/engine/note-vocabulary.mjs +161 -1
- package/engine/path-charset.mjs +120 -0
- package/engine/project-init.mjs +4 -29
- package/engine/sql-tables.mjs +311 -21
- package/engine/word-classes.mjs +167 -0
- package/engine/word-rows.mjs +233 -0
- package/engine/word-terms.mjs +306 -0
- package/package.json +2 -4
- package/types/engine/cdn-publish.d.mts +26 -25
- package/types/engine/content-index.d.mts +15 -5
- package/types/engine/culture-reach.d.mts +66 -0
- package/types/engine/dictionary-layout.d.mts +69 -0
- package/types/engine/dictionary-rows.d.mts +39 -0
- package/types/engine/glossary-entries.d.mts +12 -0
- package/types/engine/glossary-language.d.mts +42 -0
- package/types/engine/index.d.mts +1 -0
- package/types/engine/language-words.d.mts +33 -0
- package/types/engine/literature-notes.d.mts +2 -2
- package/types/engine/note-addresses.d.mts +17 -0
- package/types/engine/path-charset.d.mts +38 -0
- package/types/engine/sql-tables.d.mts +7 -2
- package/types/engine/word-classes.d.mts +87 -0
- package/types/engine/word-rows.d.mts +64 -0
- package/types/engine/word-terms.d.mts +68 -0
- package/labels.mjs +0 -191
- package/types/labels.d.mts +0 -40
package/labels.mjs
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
-
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
-
*
|
|
5
|
-
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
-
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
-
*
|
|
8
|
-
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
-
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
-
*
|
|
11
|
-
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The label registry has two faces that must agree.
|
|
16
|
-
*
|
|
17
|
-
* `.github/labels.yml` is the machine source synced to GitHub, and the §3 table
|
|
18
|
-
* in `.github/ISSUE_REPORTING.md` is the documented reference a person reads.
|
|
19
|
-
* Neither is derived from the other, so either can drift — or invent a label
|
|
20
|
-
* the other has never heard of — and nothing notices until someone files an
|
|
21
|
-
* issue against a label that does not exist, or the sync pushes a label the
|
|
22
|
-
* documentation never mentions.
|
|
23
|
-
*
|
|
24
|
-
* **This lives here because every repository wants it and only the paths ever
|
|
25
|
-
* differed.** It was a `utils/check-labels.mjs` copied per repository, which is
|
|
26
|
-
* the shape a shared check takes just before it starts drifting between copies
|
|
27
|
-
* — the same argument that moved the no-attribution check to a shared action.
|
|
28
|
-
*
|
|
29
|
-
* The checks are pure and text-in: the caller reads the two files and decides
|
|
30
|
-
* what to do with the findings, so this module needs no filesystem and is
|
|
31
|
-
* testable without one.
|
|
32
|
-
*
|
|
33
|
-
* @module
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
import { parse } from "yaml";
|
|
37
|
-
|
|
38
|
-
/** GitHub rejects a label description beyond this with a 422. */
|
|
39
|
-
export const MAX_DESCRIPTION = 100;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The line a label's `name:` entry sits on, 1-based.
|
|
43
|
-
*
|
|
44
|
-
* Located rather than tracked, because `yaml`'s document API would have to be
|
|
45
|
-
* threaded through the parse for a value this simple to recover: a registry is
|
|
46
|
-
* a flat list of `- name: <label>`, so the first line declaring that name is
|
|
47
|
-
* the entry. A name that cannot be found yields no position, which
|
|
48
|
-
* `reportFindings` drops rather than guessing.
|
|
49
|
-
*
|
|
50
|
-
* @param {string} text - The registry file's contents.
|
|
51
|
-
* @param {string} name - The label name.
|
|
52
|
-
* @returns {{line?: number, column?: number}} Spreadable position fields.
|
|
53
|
-
*/
|
|
54
|
-
function positionOfLabel(text, name) {
|
|
55
|
-
const lines = text.split("\n");
|
|
56
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
57
|
-
const m = lines[i].match(/^\s*-?\s*name:\s*["']?(.+?)["']?\s*$/);
|
|
58
|
-
if (m && m[1] === name) return { line: i + 1, column: lines[i].indexOf(name) + 1 };
|
|
59
|
-
}
|
|
60
|
-
return {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The line a label's row sits on in the documented table, 1-based.
|
|
65
|
-
*
|
|
66
|
-
* @param {string} text - The documentation file's contents.
|
|
67
|
-
* @param {string} name - The label name.
|
|
68
|
-
* @returns {{line?: number, column?: number}} Spreadable position fields.
|
|
69
|
-
*/
|
|
70
|
-
function positionOfDocRow(text, name) {
|
|
71
|
-
const lines = text.split("\n");
|
|
72
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
73
|
-
const m = lines[i].match(/^\|\s*`([a-z][a-z0-9-]*)`\s*\|/);
|
|
74
|
-
if (m && m[1] === name) return { line: i + 1, column: lines[i].indexOf(name) + 1 };
|
|
75
|
-
}
|
|
76
|
-
return {};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The label names the documented §3 table lists.
|
|
81
|
-
*
|
|
82
|
-
* A registry row is a table row whose first cell is a backticked label name,
|
|
83
|
-
* which is narrow on purpose: §3 carries prose and other tables, and a looser
|
|
84
|
-
* match would read a heading or an example as a label.
|
|
85
|
-
*
|
|
86
|
-
* @param {string} text - The documentation file's contents.
|
|
87
|
-
* @returns {{names: Set<string>, found: boolean}} The names, and whether §3 was
|
|
88
|
-
* located at all — an absent section is a different failure from an empty one.
|
|
89
|
-
*/
|
|
90
|
-
export function documentedLabels(text) {
|
|
91
|
-
const lines = text.split("\n");
|
|
92
|
-
const start = lines.findIndex((l) => /^##\s+3\./.test(l));
|
|
93
|
-
if (start < 0) return { names: new Set(), found: false };
|
|
94
|
-
const after = lines.findIndex((l, i) => i > start && /^##\s+\d/.test(l));
|
|
95
|
-
const section = lines.slice(start, after < 0 ? lines.length : after);
|
|
96
|
-
const names = new Set();
|
|
97
|
-
for (const line of section) {
|
|
98
|
-
const m = line.match(/^\|\s*`([a-z][a-z0-9-]*)`\s*\|/);
|
|
99
|
-
if (m) names.add(m[1]);
|
|
100
|
-
}
|
|
101
|
-
return { names, found: true };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check the machine registry against the documented table.
|
|
106
|
-
*
|
|
107
|
-
* Findings are returned per file rather than merged, because each names a
|
|
108
|
-
* position in a different document and the caller reports them against the
|
|
109
|
-
* file they belong to.
|
|
110
|
-
*
|
|
111
|
-
* @param {object} sources
|
|
112
|
-
* @param {string} sources.registryText - `.github/labels.yml`.
|
|
113
|
-
* @param {string} sources.docText - `.github/ISSUE_REPORTING.md`.
|
|
114
|
-
* @param {string} [sources.docPath] - The doc's path, for the message naming it.
|
|
115
|
-
* @returns {{registry: object[], doc: object[], count: number}} Findings for
|
|
116
|
-
* each file, and how many labels the registry declares.
|
|
117
|
-
*/
|
|
118
|
-
export function checkLabelRegistry({
|
|
119
|
-
registryText,
|
|
120
|
-
docText,
|
|
121
|
-
docPath = ".github/ISSUE_REPORTING.md",
|
|
122
|
-
}) {
|
|
123
|
-
const registryFindings = [];
|
|
124
|
-
const docFindings = [];
|
|
125
|
-
|
|
126
|
-
let entries;
|
|
127
|
-
try {
|
|
128
|
-
entries = parse(registryText);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
registryFindings.push({
|
|
131
|
-
severity: "error",
|
|
132
|
-
message: `label registry is not valid YAML — ${error.message}`,
|
|
133
|
-
});
|
|
134
|
-
return { registry: registryFindings, doc: docFindings, count: 0 };
|
|
135
|
-
}
|
|
136
|
-
if (!Array.isArray(entries)) {
|
|
137
|
-
registryFindings.push({
|
|
138
|
-
severity: "error",
|
|
139
|
-
message: "label registry must be a list of `{ name, description }` entries",
|
|
140
|
-
});
|
|
141
|
-
return { registry: registryFindings, doc: docFindings, count: 0 };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Caught here rather than mid-sync, where it surfaces as a bare 422 from
|
|
145
|
-
// the GitHub API naming neither the label nor the limit.
|
|
146
|
-
for (const entry of entries) {
|
|
147
|
-
const length = (entry?.description ?? "").length;
|
|
148
|
-
if (length > MAX_DESCRIPTION) {
|
|
149
|
-
registryFindings.push({
|
|
150
|
-
...positionOfLabel(registryText, entry.name),
|
|
151
|
-
severity: "error",
|
|
152
|
-
message:
|
|
153
|
-
`"${entry.name}" has a ${length}-character description; ` +
|
|
154
|
-
`GitHub rejects anything over ${MAX_DESCRIPTION}`,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const registryNames = new Set(entries.map((e) => e?.name).filter(Boolean));
|
|
160
|
-
const { names: docNames, found } = documentedLabels(docText);
|
|
161
|
-
if (!found) {
|
|
162
|
-
docFindings.push({
|
|
163
|
-
severity: "error",
|
|
164
|
-
message: `no §3 section found in ${docPath}, so the registry has nothing to agree with`,
|
|
165
|
-
});
|
|
166
|
-
return { registry: registryFindings, doc: docFindings, count: registryNames.size };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
for (const name of registryNames) {
|
|
170
|
-
if (docNames.has(name)) continue;
|
|
171
|
-
registryFindings.push({
|
|
172
|
-
...positionOfLabel(registryText, name),
|
|
173
|
-
severity: "error",
|
|
174
|
-
message:
|
|
175
|
-
`"${name}" is in the registry but not in ${docPath} §3; ` +
|
|
176
|
-
`the two are edited together, so add the row`,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
for (const name of docNames) {
|
|
180
|
-
if (registryNames.has(name)) continue;
|
|
181
|
-
docFindings.push({
|
|
182
|
-
...positionOfDocRow(docText, name),
|
|
183
|
-
severity: "error",
|
|
184
|
-
message:
|
|
185
|
-
`"${name}" is documented in §3 but absent from the registry, ` +
|
|
186
|
-
`so it is never synced and an issue cannot carry it`,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return { registry: registryFindings, doc: docFindings, count: registryNames.size };
|
|
191
|
-
}
|
package/types/labels.d.mts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The label names the documented §3 table lists.
|
|
3
|
-
*
|
|
4
|
-
* A registry row is a table row whose first cell is a backticked label name,
|
|
5
|
-
* which is narrow on purpose: §3 carries prose and other tables, and a looser
|
|
6
|
-
* match would read a heading or an example as a label.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} text - The documentation file's contents.
|
|
9
|
-
* @returns {{names: Set<string>, found: boolean}} The names, and whether §3 was
|
|
10
|
-
* located at all — an absent section is a different failure from an empty one.
|
|
11
|
-
*/
|
|
12
|
-
export function documentedLabels(text: string): {
|
|
13
|
-
names: Set<string>;
|
|
14
|
-
found: boolean;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Check the machine registry against the documented table.
|
|
18
|
-
*
|
|
19
|
-
* Findings are returned per file rather than merged, because each names a
|
|
20
|
-
* position in a different document and the caller reports them against the
|
|
21
|
-
* file they belong to.
|
|
22
|
-
*
|
|
23
|
-
* @param {object} sources
|
|
24
|
-
* @param {string} sources.registryText - `.github/labels.yml`.
|
|
25
|
-
* @param {string} sources.docText - `.github/ISSUE_REPORTING.md`.
|
|
26
|
-
* @param {string} [sources.docPath] - The doc's path, for the message naming it.
|
|
27
|
-
* @returns {{registry: object[], doc: object[], count: number}} Findings for
|
|
28
|
-
* each file, and how many labels the registry declares.
|
|
29
|
-
*/
|
|
30
|
-
export function checkLabelRegistry({ registryText, docText, docPath, }: {
|
|
31
|
-
registryText: string;
|
|
32
|
-
docText: string;
|
|
33
|
-
docPath?: string | undefined;
|
|
34
|
-
}): {
|
|
35
|
-
registry: object[];
|
|
36
|
-
doc: object[];
|
|
37
|
-
count: number;
|
|
38
|
-
};
|
|
39
|
-
/** GitHub rejects a label description beyond this with a 422. */
|
|
40
|
-
export const MAX_DESCRIPTION: 100;
|