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.
Files changed (51) hide show
  1. package/README.md +5 -0
  2. package/dist/chunk-22LFZL7L.js +109 -0
  3. package/dist/chunk-22LFZL7L.js.map +1 -0
  4. package/dist/chunk-7Z37JEHW.js +257 -0
  5. package/dist/chunk-7Z37JEHW.js.map +1 -0
  6. package/dist/chunk-WYKSBP3X.js +331 -0
  7. package/dist/chunk-WYKSBP3X.js.map +1 -0
  8. package/dist/client/index.cjs +55 -0
  9. package/dist/client/index.cjs.map +1 -0
  10. package/dist/client/index.js +10 -21
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/components/GlossaryPage.cjs +131 -0
  13. package/dist/components/GlossaryPage.cjs.map +1 -0
  14. package/dist/components/GlossaryPage.js +75 -113
  15. package/dist/components/GlossaryPage.js.map +1 -0
  16. package/dist/index.cjs +724 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.d.cts +176 -0
  19. package/dist/index.d.ts +85 -11
  20. package/dist/index.js +23 -173
  21. package/dist/index.js.map +1 -0
  22. package/dist/preset.cjs +775 -0
  23. package/dist/preset.cjs.map +1 -0
  24. package/dist/preset.d.cts +98 -0
  25. package/dist/preset.d.ts +8 -7
  26. package/dist/preset.js +79 -143
  27. package/dist/preset.js.map +1 -0
  28. package/dist/remark/glossary-terms.cjs +365 -0
  29. package/dist/remark/glossary-terms.cjs.map +1 -0
  30. package/dist/remark/glossary-terms.js +9 -440
  31. package/dist/remark/glossary-terms.js.map +1 -0
  32. package/dist/{theme/GlossaryTerm/styles.module.css → styles.module-QQW7ISLV.module.css} +2 -4
  33. package/dist/theme/GlossaryTerm/index.cjs +138 -0
  34. package/dist/theme/GlossaryTerm/index.cjs.map +1 -0
  35. package/dist/theme/GlossaryTerm/index.js +56 -90
  36. package/dist/theme/GlossaryTerm/index.js.map +1 -0
  37. package/dist/validation.cjs +283 -0
  38. package/dist/validation.cjs.map +1 -0
  39. package/dist/validation.d.cts +2 -0
  40. package/dist/validation.d.ts +2 -44
  41. package/dist/validation.js +11 -256
  42. package/dist/validation.js.map +1 -0
  43. package/package.json +27 -32
  44. package/dist/components/GlossaryPage.test.js +0 -205
  45. package/dist/index.d.ts.map +0 -1
  46. package/dist/preset.d.ts.map +0 -1
  47. package/dist/remark/glossary-terms.d.ts +0 -28
  48. package/dist/remark/glossary-terms.d.ts.map +0 -1
  49. package/dist/theme/GlossaryTerm/index.test.js +0 -143
  50. package/dist/validation.d.ts.map +0 -1
  51. /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