docusaurus-plugin-glossary 3.1.0 → 3.3.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/README.md +5 -0
- package/dist/chunk-22LFZL7L.js +109 -0
- package/dist/chunk-22LFZL7L.js.map +1 -0
- package/dist/chunk-7Z37JEHW.js +257 -0
- package/dist/chunk-7Z37JEHW.js.map +1 -0
- package/dist/chunk-WYKSBP3X.js +331 -0
- package/dist/chunk-WYKSBP3X.js.map +1 -0
- package/dist/client/index.cjs +55 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.js +10 -21
- package/dist/client/index.js.map +1 -0
- package/dist/components/GlossaryPage.cjs +131 -0
- package/dist/components/GlossaryPage.cjs.map +1 -0
- package/dist/components/GlossaryPage.js +75 -113
- package/dist/components/GlossaryPage.js.map +1 -0
- package/dist/index.cjs +724 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +176 -0
- package/dist/index.d.ts +85 -11
- package/dist/index.js +23 -173
- package/dist/index.js.map +1 -0
- package/dist/preset.cjs +775 -0
- package/dist/preset.cjs.map +1 -0
- package/dist/preset.d.cts +98 -0
- package/dist/preset.d.ts +8 -7
- package/dist/preset.js +79 -143
- package/dist/preset.js.map +1 -0
- package/dist/remark/glossary-terms.cjs +365 -0
- package/dist/remark/glossary-terms.cjs.map +1 -0
- package/dist/remark/glossary-terms.js +9 -440
- package/dist/remark/glossary-terms.js.map +1 -0
- package/dist/{theme/GlossaryTerm/styles.module.css → styles.module-QQW7ISLV.module.css} +2 -4
- package/dist/theme/GlossaryTerm/index.cjs +138 -0
- package/dist/theme/GlossaryTerm/index.cjs.map +1 -0
- package/dist/theme/GlossaryTerm/index.js +56 -90
- package/dist/theme/GlossaryTerm/index.js.map +1 -0
- package/dist/validation.cjs +283 -0
- package/dist/validation.cjs.map +1 -0
- package/dist/validation.d.cts +2 -0
- package/dist/validation.d.ts +2 -44
- package/dist/validation.js +11 -256
- package/dist/validation.js.map +1 -0
- package/package.json +27 -32
- package/dist/components/GlossaryPage.test.js +0 -205
- package/dist/index.d.ts.map +0 -1
- package/dist/preset.d.ts.map +0 -1
- package/dist/remark/glossary-terms.d.ts +0 -28
- package/dist/remark/glossary-terms.d.ts.map +0 -1
- package/dist/theme/GlossaryTerm/index.test.js +0 -143
- package/dist/validation.d.ts.map +0 -1
- /package/dist/{components/GlossaryPage.module.css → GlossaryPage.module-M4DEUP4X.module.css} +0 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
GlossaryValidationError: () => GlossaryValidationError,
|
|
34
|
+
clearGlossaryCache: () => clearGlossaryCache,
|
|
35
|
+
default: () => glossaryPlugin,
|
|
36
|
+
formatValidationErrors: () => formatValidationErrors,
|
|
37
|
+
getRemarkPlugin: () => getRemarkPlugin,
|
|
38
|
+
remarkPlugin: () => remarkPlugin,
|
|
39
|
+
validateGlossaryData: () => validateGlossaryData
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(src_exports);
|
|
42
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
43
|
+
var import_url = require("url");
|
|
44
|
+
var import_fs_extra = __toESM(require("fs-extra"), 1);
|
|
45
|
+
var import_validate_peer_dependencies = __toESM(require("validate-peer-dependencies"), 1);
|
|
46
|
+
|
|
47
|
+
// src/remark/glossary-terms.js
|
|
48
|
+
var import_unist_util_visit = require("unist-util-visit");
|
|
49
|
+
var import_path = __toESM(require("path"), 1);
|
|
50
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
51
|
+
function validateGlossaryTerms(data, _filePath) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
const validTerms = [];
|
|
54
|
+
if (data === null || data === void 0) {
|
|
55
|
+
errors.push(`Glossary data is null or undefined`);
|
|
56
|
+
return { terms: [], errors };
|
|
57
|
+
}
|
|
58
|
+
if (typeof data !== "object") {
|
|
59
|
+
errors.push(`Glossary data must be an object, got ${typeof data}`);
|
|
60
|
+
return { terms: [], errors };
|
|
61
|
+
}
|
|
62
|
+
if (!("terms" in data)) {
|
|
63
|
+
errors.push(`Glossary data must contain a "terms" array`);
|
|
64
|
+
return { terms: [], errors };
|
|
65
|
+
}
|
|
66
|
+
if (!Array.isArray(data.terms)) {
|
|
67
|
+
errors.push(`Field "terms" must be an array, got ${typeof data.terms}`);
|
|
68
|
+
return { terms: [], errors };
|
|
69
|
+
}
|
|
70
|
+
data.terms.forEach((term, index) => {
|
|
71
|
+
if (term === null || term === void 0 || typeof term !== "object") {
|
|
72
|
+
errors.push(`terms[${index}]: Term must be an object`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (typeof term.term !== "string" || term.term.trim() === "") {
|
|
76
|
+
errors.push(`terms[${index}]: Missing or invalid "term" field`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (typeof term.definition !== "string") {
|
|
80
|
+
errors.push(`terms[${index}]: Missing or invalid "definition" field`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
validTerms.push(term);
|
|
84
|
+
});
|
|
85
|
+
return { terms: validTerms, errors };
|
|
86
|
+
}
|
|
87
|
+
var glossaryCache = /* @__PURE__ */ new Map();
|
|
88
|
+
var CACHE_TTL = 5e3;
|
|
89
|
+
function remarkGlossaryTerms({
|
|
90
|
+
terms = [],
|
|
91
|
+
glossaryPath = null,
|
|
92
|
+
routePath = "/glossary",
|
|
93
|
+
siteDir = null
|
|
94
|
+
} = {}) {
|
|
95
|
+
let glossaryTerms = terms;
|
|
96
|
+
if (!glossaryTerms.length && glossaryPath && siteDir) {
|
|
97
|
+
try {
|
|
98
|
+
const glossaryFilePath = import_path.default.resolve(siteDir, glossaryPath);
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const cached = glossaryCache.get(glossaryFilePath);
|
|
101
|
+
if (cached && now - cached.loadedAt < CACHE_TTL) {
|
|
102
|
+
glossaryTerms = cached.terms;
|
|
103
|
+
} else {
|
|
104
|
+
if (import_fs.default.existsSync(glossaryFilePath)) {
|
|
105
|
+
const fileContent = import_fs.default.readFileSync(glossaryFilePath, "utf8");
|
|
106
|
+
let glossaryData;
|
|
107
|
+
try {
|
|
108
|
+
glossaryData = JSON.parse(fileContent);
|
|
109
|
+
} catch (parseError) {
|
|
110
|
+
console.error(
|
|
111
|
+
`[glossary-plugin] Failed to parse glossary JSON at ${glossaryPath}:`,
|
|
112
|
+
parseError.message
|
|
113
|
+
);
|
|
114
|
+
glossaryCache.set(glossaryFilePath, {
|
|
115
|
+
terms: [],
|
|
116
|
+
loadedAt: now
|
|
117
|
+
});
|
|
118
|
+
return (tree) => tree;
|
|
119
|
+
}
|
|
120
|
+
const { terms: validTerms, errors } = validateGlossaryTerms(glossaryData, glossaryPath);
|
|
121
|
+
if (errors.length > 0) {
|
|
122
|
+
console.warn(`[glossary-plugin] Glossary validation errors in ${glossaryPath}:`);
|
|
123
|
+
errors.forEach((err) => console.warn(` - ${err}`));
|
|
124
|
+
if (validTerms.length > 0) {
|
|
125
|
+
console.warn(`[glossary-plugin] Proceeding with ${validTerms.length} valid term(s).`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
glossaryTerms = validTerms;
|
|
129
|
+
glossaryCache.set(glossaryFilePath, {
|
|
130
|
+
terms: glossaryTerms,
|
|
131
|
+
loadedAt: now
|
|
132
|
+
});
|
|
133
|
+
if (!cached && process.env.NODE_ENV !== "production") {
|
|
134
|
+
console.log(
|
|
135
|
+
`[glossary-plugin] Loaded ${glossaryTerms.length} terms from ${glossaryPath}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
glossaryCache.set(glossaryFilePath, {
|
|
140
|
+
terms: [],
|
|
141
|
+
loadedAt: now
|
|
142
|
+
});
|
|
143
|
+
if (process.env.NODE_ENV !== "production") {
|
|
144
|
+
console.warn(`[glossary-plugin] Glossary file not found: ${glossaryPath}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.warn(
|
|
150
|
+
`[glossary-plugin] Failed to load glossary from ${glossaryPath}:`,
|
|
151
|
+
error.message
|
|
152
|
+
);
|
|
153
|
+
if (glossaryPath && siteDir) {
|
|
154
|
+
const glossaryFilePath = import_path.default.resolve(siteDir, glossaryPath);
|
|
155
|
+
glossaryCache.set(glossaryFilePath, {
|
|
156
|
+
terms: [],
|
|
157
|
+
loadedAt: Date.now()
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const termMap = /* @__PURE__ */ new Map();
|
|
163
|
+
glossaryTerms.forEach((termObj) => {
|
|
164
|
+
if (!termObj.term || termObj.autoLink === false) return;
|
|
165
|
+
const register = (phrase) => {
|
|
166
|
+
if (typeof phrase !== "string" || phrase.trim() === "") return;
|
|
167
|
+
const key = phrase.toLowerCase();
|
|
168
|
+
if (!termMap.has(key)) {
|
|
169
|
+
termMap.set(key, { termObj, matchText: phrase });
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
register(termObj.term);
|
|
173
|
+
if (Array.isArray(termObj.aliases)) {
|
|
174
|
+
termObj.aliases.forEach(register);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
const sortedTerms = Array.from(termMap.entries()).sort((a, b) => b[0].length - a[0].length);
|
|
178
|
+
if (sortedTerms.length === 0) {
|
|
179
|
+
return (tree) => tree;
|
|
180
|
+
}
|
|
181
|
+
function replaceTermsInText(text) {
|
|
182
|
+
if (!text || !sortedTerms.length) {
|
|
183
|
+
return [{ type: "text", value: text }];
|
|
184
|
+
}
|
|
185
|
+
const result = [];
|
|
186
|
+
let lastIndex = 0;
|
|
187
|
+
const textLower = text.toLowerCase();
|
|
188
|
+
const matches = [];
|
|
189
|
+
for (const [lowerPhrase, { termObj }] of sortedTerms) {
|
|
190
|
+
let searchIndex = 0;
|
|
191
|
+
while (searchIndex < textLower.length) {
|
|
192
|
+
const index = textLower.indexOf(lowerPhrase, searchIndex);
|
|
193
|
+
if (index === -1) break;
|
|
194
|
+
const beforeChar = index > 0 ? textLower[index - 1] : " ";
|
|
195
|
+
const afterIndex = index + lowerPhrase.length;
|
|
196
|
+
const afterChar = afterIndex < textLower.length ? textLower[afterIndex] : " ";
|
|
197
|
+
let matchLength = lowerPhrase.length;
|
|
198
|
+
let isWordBoundary = !/\w/.test(beforeChar) && !/\w/.test(afterChar);
|
|
199
|
+
if (!isWordBoundary && afterChar === "s") {
|
|
200
|
+
const nextChar = afterIndex + 1 < textLower.length ? textLower[afterIndex + 1] : " ";
|
|
201
|
+
if (!/\w/.test(nextChar)) {
|
|
202
|
+
isWordBoundary = true;
|
|
203
|
+
matchLength = lowerPhrase.length + 1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!isWordBoundary && afterChar === "e" && afterIndex + 1 < textLower.length && textLower[afterIndex + 1] === "s") {
|
|
207
|
+
const nextChar = afterIndex + 2 < textLower.length ? textLower[afterIndex + 2] : " ";
|
|
208
|
+
if (!/\w/.test(nextChar)) {
|
|
209
|
+
isWordBoundary = true;
|
|
210
|
+
matchLength = lowerPhrase.length + 2;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (isWordBoundary) {
|
|
214
|
+
matches.push({
|
|
215
|
+
index,
|
|
216
|
+
length: matchLength,
|
|
217
|
+
termObj,
|
|
218
|
+
// Store original case from the text (what the reader actually wrote)
|
|
219
|
+
originalText: text.substring(index, index + matchLength)
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
searchIndex = index + 1;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
matches.sort((a, b) => a.index - b.index);
|
|
226
|
+
const nonOverlappingMatches = [];
|
|
227
|
+
let lastMatchEnd = 0;
|
|
228
|
+
for (const match of matches) {
|
|
229
|
+
if (match.index >= lastMatchEnd) {
|
|
230
|
+
nonOverlappingMatches.push(match);
|
|
231
|
+
lastMatchEnd = match.index + match.length;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (const match of nonOverlappingMatches) {
|
|
235
|
+
if (match.index > lastIndex) {
|
|
236
|
+
result.push({
|
|
237
|
+
type: "text",
|
|
238
|
+
value: text.substring(lastIndex, match.index)
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
result.push({
|
|
242
|
+
type: "mdxJsxFlowElement",
|
|
243
|
+
name: "GlossaryTerm",
|
|
244
|
+
attributes: [
|
|
245
|
+
{
|
|
246
|
+
type: "mdxJsxAttribute",
|
|
247
|
+
name: "term",
|
|
248
|
+
value: match.termObj.term
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
type: "mdxJsxAttribute",
|
|
252
|
+
name: "definition",
|
|
253
|
+
value: match.termObj.definition || ""
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
type: "mdxJsxAttribute",
|
|
257
|
+
name: "routePath",
|
|
258
|
+
value: routePath
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
children: [
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
value: match.originalText
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
});
|
|
268
|
+
lastIndex = match.index + match.length;
|
|
269
|
+
}
|
|
270
|
+
if (lastIndex < text.length) {
|
|
271
|
+
result.push({
|
|
272
|
+
type: "text",
|
|
273
|
+
value: text.substring(lastIndex)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return result.length > 0 ? result : [{ type: "text", value: text }];
|
|
277
|
+
}
|
|
278
|
+
function collectHeadingTextNodes(tree) {
|
|
279
|
+
const skip = /* @__PURE__ */ new WeakSet();
|
|
280
|
+
(0, import_unist_util_visit.visit)(tree, "heading", (headingNode) => {
|
|
281
|
+
(0, import_unist_util_visit.visit)(headingNode, "text", (textNode) => {
|
|
282
|
+
skip.add(textNode);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
return skip;
|
|
286
|
+
}
|
|
287
|
+
const transformer = (tree) => {
|
|
288
|
+
let usedGlossaryTerm = false;
|
|
289
|
+
const textNodesInHeadings = collectHeadingTextNodes(tree);
|
|
290
|
+
(0, import_unist_util_visit.visit)(tree, "text", (node, index, parent) => {
|
|
291
|
+
if (parent.type === "code" || parent.type === "inlineCode" || parent.type === "link" || parent.type === "mdxJsxFlowElement" || parent.type === "mdxJsxTextElement") {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (textNodesInHeadings.has(node)) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const replacements = replaceTermsInText(node.value);
|
|
298
|
+
if (replacements.length > 1 || replacements.length === 1 && replacements[0].type !== "text") {
|
|
299
|
+
const newNodes = replacements.map((replacement) => {
|
|
300
|
+
if (replacement.type === "mdxJsxFlowElement") {
|
|
301
|
+
if (parent.type === "paragraph") {
|
|
302
|
+
usedGlossaryTerm = true;
|
|
303
|
+
return {
|
|
304
|
+
type: "mdxJsxTextElement",
|
|
305
|
+
name: replacement.name,
|
|
306
|
+
attributes: replacement.attributes,
|
|
307
|
+
children: replacement.children
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
usedGlossaryTerm = true;
|
|
311
|
+
}
|
|
312
|
+
return replacement;
|
|
313
|
+
});
|
|
314
|
+
parent.children.splice(index, 1, ...newNodes);
|
|
315
|
+
return index + newNodes.length - 1;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
if (usedGlossaryTerm) {
|
|
319
|
+
const importNode = {
|
|
320
|
+
type: "mdxjsEsm",
|
|
321
|
+
value: 'import GlossaryTerm from "@theme/GlossaryTerm";',
|
|
322
|
+
data: {
|
|
323
|
+
estree: {
|
|
324
|
+
type: "Program",
|
|
325
|
+
sourceType: "module",
|
|
326
|
+
body: [
|
|
327
|
+
{
|
|
328
|
+
type: "ImportDeclaration",
|
|
329
|
+
specifiers: [
|
|
330
|
+
{
|
|
331
|
+
type: "ImportDefaultSpecifier",
|
|
332
|
+
local: { type: "Identifier", name: "GlossaryTerm" }
|
|
333
|
+
}
|
|
334
|
+
],
|
|
335
|
+
source: {
|
|
336
|
+
type: "Literal",
|
|
337
|
+
value: "@theme/GlossaryTerm",
|
|
338
|
+
raw: '"@theme/GlossaryTerm"'
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const hasImport = Array.isArray(tree.children) && tree.children.some(
|
|
346
|
+
(n) => n.type === "mdxjsEsm" && (n.value?.includes("@theme/GlossaryTerm") || n.data?.estree?.body?.some((s) => s.source?.value === "@theme/GlossaryTerm"))
|
|
347
|
+
);
|
|
348
|
+
if (!hasImport) {
|
|
349
|
+
if (!Array.isArray(tree.children)) tree.children = [];
|
|
350
|
+
let insertIndex = 0;
|
|
351
|
+
for (let i = 0; i < tree.children.length; i++) {
|
|
352
|
+
const node = tree.children[i];
|
|
353
|
+
if (node.type === "yaml" || node.type === "toml") {
|
|
354
|
+
insertIndex = i + 1;
|
|
355
|
+
} else {
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
tree.children.splice(insertIndex, 0, importNode);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
return transformer;
|
|
364
|
+
}
|
|
365
|
+
function clearGlossaryCache(filePath) {
|
|
366
|
+
if (filePath) {
|
|
367
|
+
glossaryCache.delete(filePath);
|
|
368
|
+
} else {
|
|
369
|
+
glossaryCache.clear();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/validation.ts
|
|
374
|
+
function validateTerm(term, index) {
|
|
375
|
+
const errors = [];
|
|
376
|
+
const prefix = `terms[${index}]`;
|
|
377
|
+
if (term === null || term === void 0) {
|
|
378
|
+
errors.push({
|
|
379
|
+
field: prefix,
|
|
380
|
+
message: "Term cannot be null or undefined",
|
|
381
|
+
value: term
|
|
382
|
+
});
|
|
383
|
+
return errors;
|
|
384
|
+
}
|
|
385
|
+
if (typeof term !== "object") {
|
|
386
|
+
errors.push({
|
|
387
|
+
field: prefix,
|
|
388
|
+
message: `Term must be an object, got ${typeof term}`,
|
|
389
|
+
value: term
|
|
390
|
+
});
|
|
391
|
+
return errors;
|
|
392
|
+
}
|
|
393
|
+
const termObj = term;
|
|
394
|
+
if (!("term" in termObj)) {
|
|
395
|
+
errors.push({
|
|
396
|
+
field: `${prefix}.term`,
|
|
397
|
+
message: 'Missing required field "term"'
|
|
398
|
+
});
|
|
399
|
+
} else if (typeof termObj.term !== "string") {
|
|
400
|
+
errors.push({
|
|
401
|
+
field: `${prefix}.term`,
|
|
402
|
+
message: `Field "term" must be a string, got ${typeof termObj.term}`,
|
|
403
|
+
value: termObj.term
|
|
404
|
+
});
|
|
405
|
+
} else if (termObj.term.trim() === "") {
|
|
406
|
+
errors.push({
|
|
407
|
+
field: `${prefix}.term`,
|
|
408
|
+
message: 'Field "term" cannot be empty',
|
|
409
|
+
value: termObj.term
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (!("definition" in termObj)) {
|
|
413
|
+
errors.push({
|
|
414
|
+
field: `${prefix}.definition`,
|
|
415
|
+
message: 'Missing required field "definition"'
|
|
416
|
+
});
|
|
417
|
+
} else if (typeof termObj.definition !== "string") {
|
|
418
|
+
errors.push({
|
|
419
|
+
field: `${prefix}.definition`,
|
|
420
|
+
message: `Field "definition" must be a string, got ${typeof termObj.definition}`,
|
|
421
|
+
value: termObj.definition
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
if ("abbreviation" in termObj && termObj.abbreviation !== void 0) {
|
|
425
|
+
if (typeof termObj.abbreviation !== "string") {
|
|
426
|
+
errors.push({
|
|
427
|
+
field: `${prefix}.abbreviation`,
|
|
428
|
+
message: `Field "abbreviation" must be a string, got ${typeof termObj.abbreviation}`,
|
|
429
|
+
value: termObj.abbreviation
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if ("relatedTerms" in termObj && termObj.relatedTerms !== void 0) {
|
|
434
|
+
if (!Array.isArray(termObj.relatedTerms)) {
|
|
435
|
+
errors.push({
|
|
436
|
+
field: `${prefix}.relatedTerms`,
|
|
437
|
+
message: `Field "relatedTerms" must be an array, got ${typeof termObj.relatedTerms}`,
|
|
438
|
+
value: termObj.relatedTerms
|
|
439
|
+
});
|
|
440
|
+
} else {
|
|
441
|
+
termObj.relatedTerms.forEach((relatedTerm, relatedIndex) => {
|
|
442
|
+
if (typeof relatedTerm !== "string") {
|
|
443
|
+
errors.push({
|
|
444
|
+
field: `${prefix}.relatedTerms[${relatedIndex}]`,
|
|
445
|
+
message: `Related term must be a string, got ${typeof relatedTerm}`,
|
|
446
|
+
value: relatedTerm
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if ("id" in termObj && termObj.id !== void 0) {
|
|
453
|
+
if (typeof termObj.id !== "string") {
|
|
454
|
+
errors.push({
|
|
455
|
+
field: `${prefix}.id`,
|
|
456
|
+
message: `Field "id" must be a string, got ${typeof termObj.id}`,
|
|
457
|
+
value: termObj.id
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if ("autoLink" in termObj && termObj.autoLink !== void 0) {
|
|
462
|
+
if (typeof termObj.autoLink !== "boolean") {
|
|
463
|
+
errors.push({
|
|
464
|
+
field: `${prefix}.autoLink`,
|
|
465
|
+
message: `Field "autoLink" must be a boolean, got ${typeof termObj.autoLink}`,
|
|
466
|
+
value: termObj.autoLink
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if ("aliases" in termObj && termObj.aliases !== void 0) {
|
|
471
|
+
if (!Array.isArray(termObj.aliases)) {
|
|
472
|
+
errors.push({
|
|
473
|
+
field: `${prefix}.aliases`,
|
|
474
|
+
message: `Field "aliases" must be an array, got ${typeof termObj.aliases}`,
|
|
475
|
+
value: termObj.aliases
|
|
476
|
+
});
|
|
477
|
+
} else {
|
|
478
|
+
termObj.aliases.forEach((alias, aliasIndex) => {
|
|
479
|
+
if (typeof alias !== "string") {
|
|
480
|
+
errors.push({
|
|
481
|
+
field: `${prefix}.aliases[${aliasIndex}]`,
|
|
482
|
+
message: `Alias must be a string, got ${typeof alias}`,
|
|
483
|
+
value: alias
|
|
484
|
+
});
|
|
485
|
+
} else if (alias.trim() === "") {
|
|
486
|
+
errors.push({
|
|
487
|
+
field: `${prefix}.aliases[${aliasIndex}]`,
|
|
488
|
+
message: "Alias cannot be empty",
|
|
489
|
+
value: alias
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return errors;
|
|
496
|
+
}
|
|
497
|
+
function validateGlossaryData(data, options = {}) {
|
|
498
|
+
const { throwOnError = true } = options;
|
|
499
|
+
const errors = [];
|
|
500
|
+
if (data === null || data === void 0) {
|
|
501
|
+
errors.push({
|
|
502
|
+
field: "root",
|
|
503
|
+
message: "Glossary data cannot be null or undefined",
|
|
504
|
+
value: data
|
|
505
|
+
});
|
|
506
|
+
if (throwOnError && errors.length > 0) {
|
|
507
|
+
throw new GlossaryValidationError(errors);
|
|
508
|
+
}
|
|
509
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
510
|
+
}
|
|
511
|
+
if (typeof data !== "object") {
|
|
512
|
+
errors.push({
|
|
513
|
+
field: "root",
|
|
514
|
+
message: `Glossary data must be an object, got ${typeof data}`,
|
|
515
|
+
value: data
|
|
516
|
+
});
|
|
517
|
+
if (throwOnError && errors.length > 0) {
|
|
518
|
+
throw new GlossaryValidationError(errors);
|
|
519
|
+
}
|
|
520
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
521
|
+
}
|
|
522
|
+
const glossaryData = data;
|
|
523
|
+
if ("title" in glossaryData && typeof glossaryData.title !== "string") {
|
|
524
|
+
errors.push({
|
|
525
|
+
field: "title",
|
|
526
|
+
message: "The title property in the GlossaryData must be a string."
|
|
527
|
+
});
|
|
528
|
+
if (throwOnError && errors.length > 0) {
|
|
529
|
+
throw new GlossaryValidationError(errors);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const validTitle = glossaryData.title;
|
|
533
|
+
if ("description" in glossaryData && typeof glossaryData.description !== "string") {
|
|
534
|
+
errors.push({
|
|
535
|
+
field: "description",
|
|
536
|
+
message: "The description property in the GlossaryData must be a string."
|
|
537
|
+
});
|
|
538
|
+
if (throwOnError && errors.length > 0) {
|
|
539
|
+
throw new GlossaryValidationError(errors);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const validDescription = glossaryData.description;
|
|
543
|
+
if (!("terms" in glossaryData)) {
|
|
544
|
+
errors.push({
|
|
545
|
+
field: "terms",
|
|
546
|
+
message: 'Glossary data must contain a "terms" array'
|
|
547
|
+
});
|
|
548
|
+
if (throwOnError && errors.length > 0) {
|
|
549
|
+
throw new GlossaryValidationError(errors);
|
|
550
|
+
}
|
|
551
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
552
|
+
}
|
|
553
|
+
if (!Array.isArray(glossaryData.terms)) {
|
|
554
|
+
errors.push({
|
|
555
|
+
field: "terms",
|
|
556
|
+
message: `Field "terms" must be an array, got ${typeof glossaryData.terms}`,
|
|
557
|
+
value: glossaryData.terms
|
|
558
|
+
});
|
|
559
|
+
if (throwOnError && errors.length > 0) {
|
|
560
|
+
throw new GlossaryValidationError(errors);
|
|
561
|
+
}
|
|
562
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
563
|
+
}
|
|
564
|
+
const validTerms = [];
|
|
565
|
+
glossaryData.terms.forEach((term, index) => {
|
|
566
|
+
const termErrors = validateTerm(term, index);
|
|
567
|
+
if (termErrors.length > 0) {
|
|
568
|
+
errors.push(...termErrors);
|
|
569
|
+
} else {
|
|
570
|
+
validTerms.push(term);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
const termNames = /* @__PURE__ */ new Map();
|
|
574
|
+
validTerms.forEach((term, index) => {
|
|
575
|
+
const lowerName = term.term.toLowerCase();
|
|
576
|
+
if (termNames.has(lowerName)) {
|
|
577
|
+
errors.push({
|
|
578
|
+
field: `terms[${index}].term`,
|
|
579
|
+
message: `Duplicate term "${term.term}" (first occurrence at index ${termNames.get(lowerName)})`,
|
|
580
|
+
value: term.term
|
|
581
|
+
});
|
|
582
|
+
} else {
|
|
583
|
+
termNames.set(lowerName, index);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
if (throwOnError && errors.length > 0) {
|
|
587
|
+
throw new GlossaryValidationError(errors);
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
valid: errors.length === 0,
|
|
591
|
+
errors,
|
|
592
|
+
data: { title: validTitle, description: validDescription, terms: validTerms }
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
var GlossaryValidationError = class _GlossaryValidationError extends Error {
|
|
596
|
+
constructor(errors) {
|
|
597
|
+
const message = formatValidationErrors(errors);
|
|
598
|
+
super(message);
|
|
599
|
+
this.name = "GlossaryValidationError";
|
|
600
|
+
this.errors = errors;
|
|
601
|
+
if (Error.captureStackTrace) {
|
|
602
|
+
Error.captureStackTrace(this, _GlossaryValidationError);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
function formatValidationErrors(errors) {
|
|
607
|
+
if (errors.length === 0) {
|
|
608
|
+
return "No validation errors";
|
|
609
|
+
}
|
|
610
|
+
const header = `Glossary validation failed with ${errors.length} error${errors.length > 1 ? "s" : ""}:`;
|
|
611
|
+
const errorList = errors.map((err, index) => {
|
|
612
|
+
let msg = ` ${index + 1}. [${err.field}] ${err.message}`;
|
|
613
|
+
if (err.value !== void 0) {
|
|
614
|
+
const valueStr = typeof err.value === "object" ? JSON.stringify(err.value) : String(err.value);
|
|
615
|
+
const truncated = valueStr.length > 50 ? valueStr.substring(0, 50) + "..." : valueStr;
|
|
616
|
+
msg += ` (got: ${truncated})`;
|
|
617
|
+
}
|
|
618
|
+
return msg;
|
|
619
|
+
}).join("\n");
|
|
620
|
+
return `${header}
|
|
621
|
+
${errorList}`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/index.ts
|
|
625
|
+
var import_meta = {};
|
|
626
|
+
var currentFilePath = (0, import_url.fileURLToPath)(import_meta.url);
|
|
627
|
+
var currentDir = import_path2.default.dirname(currentFilePath);
|
|
628
|
+
(0, import_validate_peer_dependencies.default)(currentDir);
|
|
629
|
+
function glossaryPlugin(context, options = {}) {
|
|
630
|
+
const { glossaryPath = "glossary/glossary.json", routePath = "/glossary" } = options;
|
|
631
|
+
return {
|
|
632
|
+
name: "docusaurus-plugin-glossary",
|
|
633
|
+
getClientModules() {
|
|
634
|
+
return [import_path2.default.resolve(currentDir, "./client/index.js")];
|
|
635
|
+
},
|
|
636
|
+
async loadContent() {
|
|
637
|
+
const glossaryFilePath = import_path2.default.resolve(context.siteDir, glossaryPath);
|
|
638
|
+
if (await import_fs_extra.default.pathExists(glossaryFilePath)) {
|
|
639
|
+
try {
|
|
640
|
+
const rawData = await import_fs_extra.default.readJson(glossaryFilePath);
|
|
641
|
+
const validationResult = validateGlossaryData(rawData, { throwOnError: false });
|
|
642
|
+
if (!validationResult.valid) {
|
|
643
|
+
console.warn(
|
|
644
|
+
`[glossary-plugin] Glossary file has validation errors at ${glossaryFilePath}:`
|
|
645
|
+
);
|
|
646
|
+
validationResult.errors.forEach((err) => {
|
|
647
|
+
console.warn(` - [${err.field}] ${err.message}`);
|
|
648
|
+
});
|
|
649
|
+
console.warn("[glossary-plugin] Proceeding with valid terms only.");
|
|
650
|
+
}
|
|
651
|
+
return validationResult.data;
|
|
652
|
+
} catch (error) {
|
|
653
|
+
if (error instanceof GlossaryValidationError) {
|
|
654
|
+
throw error;
|
|
655
|
+
}
|
|
656
|
+
throw new Error(
|
|
657
|
+
`Failed to parse glossary file at ${glossaryFilePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
console.warn(`Glossary file not found at ${glossaryFilePath}. Using empty glossary.`);
|
|
662
|
+
return { terms: [] };
|
|
663
|
+
},
|
|
664
|
+
async contentLoaded({ content, actions }) {
|
|
665
|
+
const { createData, addRoute, setGlobalData } = actions;
|
|
666
|
+
const glossaryContent = content;
|
|
667
|
+
const glossaryDataPath = await createData(
|
|
668
|
+
"glossary-data.json",
|
|
669
|
+
JSON.stringify(glossaryContent)
|
|
670
|
+
);
|
|
671
|
+
await createData(
|
|
672
|
+
"remark-glossary-data.json",
|
|
673
|
+
JSON.stringify({
|
|
674
|
+
terms: glossaryContent.terms || [],
|
|
675
|
+
routePath
|
|
676
|
+
})
|
|
677
|
+
);
|
|
678
|
+
addRoute({
|
|
679
|
+
path: routePath,
|
|
680
|
+
component: import_path2.default.join(currentDir, "components/GlossaryPage.js"),
|
|
681
|
+
exact: true,
|
|
682
|
+
modules: {
|
|
683
|
+
glossaryData: glossaryDataPath
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
setGlobalData({
|
|
687
|
+
terms: glossaryContent.terms || [],
|
|
688
|
+
routePath
|
|
689
|
+
});
|
|
690
|
+
},
|
|
691
|
+
getThemePath() {
|
|
692
|
+
return import_path2.default.resolve(currentDir, "./theme");
|
|
693
|
+
},
|
|
694
|
+
getPathsToWatch() {
|
|
695
|
+
return [import_path2.default.resolve(context.siteDir, glossaryPath)];
|
|
696
|
+
},
|
|
697
|
+
async postBuild() {
|
|
698
|
+
console.log("Glossary plugin: Build completed");
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
var remarkPlugin = remarkGlossaryTerms;
|
|
703
|
+
function getRemarkPlugin(pluginOptions, context) {
|
|
704
|
+
const { glossaryPath = "glossary/glossary.json", routePath = "/glossary" } = pluginOptions;
|
|
705
|
+
const siteDir = context?.siteDir;
|
|
706
|
+
return [
|
|
707
|
+
remarkGlossaryTerms,
|
|
708
|
+
{
|
|
709
|
+
glossaryPath,
|
|
710
|
+
routePath,
|
|
711
|
+
siteDir
|
|
712
|
+
}
|
|
713
|
+
];
|
|
714
|
+
}
|
|
715
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
716
|
+
0 && (module.exports = {
|
|
717
|
+
GlossaryValidationError,
|
|
718
|
+
clearGlossaryCache,
|
|
719
|
+
formatValidationErrors,
|
|
720
|
+
getRemarkPlugin,
|
|
721
|
+
remarkPlugin,
|
|
722
|
+
validateGlossaryData
|
|
723
|
+
});
|
|
724
|
+
//# sourceMappingURL=index.cjs.map
|