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