docusaurus-plugin-glossary 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4CUFUKUA.js +109 -0
- package/dist/chunk-4CUFUKUA.js.map +1 -0
- package/dist/chunk-PEB4Y6RI.js +311 -0
- package/dist/chunk-PEB4Y6RI.js.map +1 -0
- package/dist/chunk-SNP37IVL.js +212 -0
- package/dist/chunk-SNP37IVL.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 +130 -0
- package/dist/components/GlossaryPage.cjs.map +1 -0
- package/dist/components/GlossaryPage.js +74 -113
- package/dist/components/GlossaryPage.js.map +1 -0
- package/dist/index.cjs +659 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +173 -0
- package/dist/index.d.ts +83 -11
- package/dist/index.js +23 -173
- package/dist/index.js.map +1 -0
- package/dist/preset.cjs +710 -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 +345 -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/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 +238 -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 -246
- package/dist/validation.js.map +1 -0
- package/package.json +25 -30
- 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/{theme/GlossaryTerm/styles.module.css → styles.module-N7ME3MWS.module.css} +0 -0
package/dist/validation.js
CHANGED
|
@@ -1,246 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
errors.push({
|
|
13
|
-
field: prefix,
|
|
14
|
-
message: 'Term cannot be null or undefined',
|
|
15
|
-
value: term,
|
|
16
|
-
});
|
|
17
|
-
return errors;
|
|
18
|
-
}
|
|
19
|
-
if (typeof term !== 'object') {
|
|
20
|
-
errors.push({
|
|
21
|
-
field: prefix,
|
|
22
|
-
message: `Term must be an object, got ${typeof term}`,
|
|
23
|
-
value: term,
|
|
24
|
-
});
|
|
25
|
-
return errors;
|
|
26
|
-
}
|
|
27
|
-
const termObj = term;
|
|
28
|
-
// Required: term (string)
|
|
29
|
-
if (!('term' in termObj)) {
|
|
30
|
-
errors.push({
|
|
31
|
-
field: `${prefix}.term`,
|
|
32
|
-
message: 'Missing required field "term"',
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
else if (typeof termObj.term !== 'string') {
|
|
36
|
-
errors.push({
|
|
37
|
-
field: `${prefix}.term`,
|
|
38
|
-
message: `Field "term" must be a string, got ${typeof termObj.term}`,
|
|
39
|
-
value: termObj.term,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
else if (termObj.term.trim() === '') {
|
|
43
|
-
errors.push({
|
|
44
|
-
field: `${prefix}.term`,
|
|
45
|
-
message: 'Field "term" cannot be empty',
|
|
46
|
-
value: termObj.term,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
// Required: definition (string)
|
|
50
|
-
if (!('definition' in termObj)) {
|
|
51
|
-
errors.push({
|
|
52
|
-
field: `${prefix}.definition`,
|
|
53
|
-
message: 'Missing required field "definition"',
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
else if (typeof termObj.definition !== 'string') {
|
|
57
|
-
errors.push({
|
|
58
|
-
field: `${prefix}.definition`,
|
|
59
|
-
message: `Field "definition" must be a string, got ${typeof termObj.definition}`,
|
|
60
|
-
value: termObj.definition,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
// Optional: abbreviation (string)
|
|
64
|
-
if ('abbreviation' in termObj && termObj.abbreviation !== undefined) {
|
|
65
|
-
if (typeof termObj.abbreviation !== 'string') {
|
|
66
|
-
errors.push({
|
|
67
|
-
field: `${prefix}.abbreviation`,
|
|
68
|
-
message: `Field "abbreviation" must be a string, got ${typeof termObj.abbreviation}`,
|
|
69
|
-
value: termObj.abbreviation,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Optional: relatedTerms (string[])
|
|
74
|
-
if ('relatedTerms' in termObj && termObj.relatedTerms !== undefined) {
|
|
75
|
-
if (!Array.isArray(termObj.relatedTerms)) {
|
|
76
|
-
errors.push({
|
|
77
|
-
field: `${prefix}.relatedTerms`,
|
|
78
|
-
message: `Field "relatedTerms" must be an array, got ${typeof termObj.relatedTerms}`,
|
|
79
|
-
value: termObj.relatedTerms,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
termObj.relatedTerms.forEach((relatedTerm, relatedIndex) => {
|
|
84
|
-
if (typeof relatedTerm !== 'string') {
|
|
85
|
-
errors.push({
|
|
86
|
-
field: `${prefix}.relatedTerms[${relatedIndex}]`,
|
|
87
|
-
message: `Related term must be a string, got ${typeof relatedTerm}`,
|
|
88
|
-
value: relatedTerm,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// Optional: id (string)
|
|
95
|
-
if ('id' in termObj && termObj.id !== undefined) {
|
|
96
|
-
if (typeof termObj.id !== 'string') {
|
|
97
|
-
errors.push({
|
|
98
|
-
field: `${prefix}.id`,
|
|
99
|
-
message: `Field "id" must be a string, got ${typeof termObj.id}`,
|
|
100
|
-
value: termObj.id,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return errors;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Validates glossary data structure
|
|
108
|
-
*
|
|
109
|
-
* Ensures the glossary data conforms to the expected schema:
|
|
110
|
-
* - Must be an object with a "terms" array
|
|
111
|
-
* - Each term must have "term" (string) and "definition" (string)
|
|
112
|
-
* - Optional fields: abbreviation (string), relatedTerms (string[]), id (string)
|
|
113
|
-
*
|
|
114
|
-
* @param data - The data to validate
|
|
115
|
-
* @param options - Validation options
|
|
116
|
-
* @param options.throwOnError - If true, throws an error on validation failure (default: true)
|
|
117
|
-
* @returns Validation result with errors and sanitized data
|
|
118
|
-
* @throws Error if data is invalid and throwOnError is true
|
|
119
|
-
*/
|
|
120
|
-
export function validateGlossaryData(data, options = {}) {
|
|
121
|
-
const { throwOnError = true } = options;
|
|
122
|
-
const errors = [];
|
|
123
|
-
// Check if data is null or undefined
|
|
124
|
-
if (data === null || data === undefined) {
|
|
125
|
-
errors.push({
|
|
126
|
-
field: 'root',
|
|
127
|
-
message: 'Glossary data cannot be null or undefined',
|
|
128
|
-
value: data,
|
|
129
|
-
});
|
|
130
|
-
if (throwOnError && errors.length > 0) {
|
|
131
|
-
throw new GlossaryValidationError(errors);
|
|
132
|
-
}
|
|
133
|
-
return { valid: false, errors, data: { terms: [] } };
|
|
134
|
-
}
|
|
135
|
-
// Check if data is an object
|
|
136
|
-
if (typeof data !== 'object') {
|
|
137
|
-
errors.push({
|
|
138
|
-
field: 'root',
|
|
139
|
-
message: `Glossary data must be an object, got ${typeof data}`,
|
|
140
|
-
value: data,
|
|
141
|
-
});
|
|
142
|
-
if (throwOnError && errors.length > 0) {
|
|
143
|
-
throw new GlossaryValidationError(errors);
|
|
144
|
-
}
|
|
145
|
-
return { valid: false, errors, data: { terms: [] } };
|
|
146
|
-
}
|
|
147
|
-
const glossaryData = data;
|
|
148
|
-
// Check for terms array
|
|
149
|
-
if (!('terms' in glossaryData)) {
|
|
150
|
-
errors.push({
|
|
151
|
-
field: 'terms',
|
|
152
|
-
message: 'Glossary data must contain a "terms" array',
|
|
153
|
-
});
|
|
154
|
-
if (throwOnError && errors.length > 0) {
|
|
155
|
-
throw new GlossaryValidationError(errors);
|
|
156
|
-
}
|
|
157
|
-
return { valid: false, errors, data: { terms: [] } };
|
|
158
|
-
}
|
|
159
|
-
if (!Array.isArray(glossaryData.terms)) {
|
|
160
|
-
errors.push({
|
|
161
|
-
field: 'terms',
|
|
162
|
-
message: `Field "terms" must be an array, got ${typeof glossaryData.terms}`,
|
|
163
|
-
value: glossaryData.terms,
|
|
164
|
-
});
|
|
165
|
-
if (throwOnError && errors.length > 0) {
|
|
166
|
-
throw new GlossaryValidationError(errors);
|
|
167
|
-
}
|
|
168
|
-
return { valid: false, errors, data: { terms: [] } };
|
|
169
|
-
}
|
|
170
|
-
// Validate each term
|
|
171
|
-
const validTerms = [];
|
|
172
|
-
glossaryData.terms.forEach((term, index) => {
|
|
173
|
-
const termErrors = validateTerm(term, index);
|
|
174
|
-
if (termErrors.length > 0) {
|
|
175
|
-
errors.push(...termErrors);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
// Term is valid, add to valid terms
|
|
179
|
-
validTerms.push(term);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
// Check for duplicate terms
|
|
183
|
-
const termNames = new Map();
|
|
184
|
-
validTerms.forEach((term, index) => {
|
|
185
|
-
const lowerName = term.term.toLowerCase();
|
|
186
|
-
if (termNames.has(lowerName)) {
|
|
187
|
-
errors.push({
|
|
188
|
-
field: `terms[${index}].term`,
|
|
189
|
-
message: `Duplicate term "${term.term}" (first occurrence at index ${termNames.get(lowerName)})`,
|
|
190
|
-
value: term.term,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
termNames.set(lowerName, index);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
if (throwOnError && errors.length > 0) {
|
|
198
|
-
throw new GlossaryValidationError(errors);
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
valid: errors.length === 0,
|
|
202
|
-
errors,
|
|
203
|
-
data: { terms: validTerms },
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Custom error class for glossary validation errors
|
|
208
|
-
* Provides detailed error messages for debugging
|
|
209
|
-
*/
|
|
210
|
-
export class GlossaryValidationError extends Error {
|
|
211
|
-
constructor(errors) {
|
|
212
|
-
const message = formatValidationErrors(errors);
|
|
213
|
-
super(message);
|
|
214
|
-
this.name = 'GlossaryValidationError';
|
|
215
|
-
this.errors = errors;
|
|
216
|
-
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
217
|
-
if (Error.captureStackTrace) {
|
|
218
|
-
Error.captureStackTrace(this, GlossaryValidationError);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Formats validation errors into a readable string
|
|
224
|
-
*
|
|
225
|
-
* @param errors - Array of validation errors
|
|
226
|
-
* @returns Formatted error message
|
|
227
|
-
*/
|
|
228
|
-
export function formatValidationErrors(errors) {
|
|
229
|
-
if (errors.length === 0) {
|
|
230
|
-
return 'No validation errors';
|
|
231
|
-
}
|
|
232
|
-
const header = `Glossary validation failed with ${errors.length} error${errors.length > 1 ? 's' : ''}:`;
|
|
233
|
-
const errorList = errors
|
|
234
|
-
.map((err, index) => {
|
|
235
|
-
let msg = ` ${index + 1}. [${err.field}] ${err.message}`;
|
|
236
|
-
if (err.value !== undefined) {
|
|
237
|
-
const valueStr = typeof err.value === 'object' ? JSON.stringify(err.value) : String(err.value);
|
|
238
|
-
// Truncate long values
|
|
239
|
-
const truncated = valueStr.length > 50 ? valueStr.substring(0, 50) + '...' : valueStr;
|
|
240
|
-
msg += ` (got: ${truncated})`;
|
|
241
|
-
}
|
|
242
|
-
return msg;
|
|
243
|
-
})
|
|
244
|
-
.join('\n');
|
|
245
|
-
return `${header}\n${errorList}`;
|
|
246
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
GlossaryValidationError,
|
|
3
|
+
formatValidationErrors,
|
|
4
|
+
validateGlossaryData
|
|
5
|
+
} from "./chunk-SNP37IVL.js";
|
|
6
|
+
export {
|
|
7
|
+
GlossaryValidationError,
|
|
8
|
+
formatValidationErrors,
|
|
9
|
+
validateGlossaryData
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docusaurus-plugin-glossary",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "A Docusaurus plugin for creating and managing glossary terms with auto-generated pages and inline tooltips",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/index.
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
7
9
|
"exports": {
|
|
8
10
|
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
9
12
|
"import": "./dist/index.js",
|
|
10
|
-
"require": "./dist/index.
|
|
11
|
-
"default": "./dist/index.js"
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
12
14
|
},
|
|
13
15
|
"./preset": {
|
|
16
|
+
"types": "./dist/preset.d.ts",
|
|
14
17
|
"import": "./dist/preset.js",
|
|
15
|
-
"require": "./dist/preset.
|
|
16
|
-
"default": "./dist/preset.js"
|
|
18
|
+
"require": "./dist/preset.cjs"
|
|
17
19
|
},
|
|
18
20
|
"./remark/glossary-terms": {
|
|
21
|
+
"types": "./dist/remark/glossary-terms.d.ts",
|
|
19
22
|
"import": "./dist/remark/glossary-terms.js",
|
|
20
|
-
"require": "./dist/remark/glossary-terms.
|
|
21
|
-
"default": "./dist/remark/glossary-terms.js"
|
|
23
|
+
"require": "./dist/remark/glossary-terms.cjs"
|
|
22
24
|
}
|
|
23
25
|
},
|
|
24
26
|
"files": [
|
|
@@ -27,8 +29,11 @@
|
|
|
27
29
|
"LICENSE"
|
|
28
30
|
],
|
|
29
31
|
"scripts": {
|
|
30
|
-
"build": "
|
|
31
|
-
"
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev": "tsup --watch",
|
|
34
|
+
"watch": "npm run dev",
|
|
35
|
+
"clean": "rimraf dist",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
32
37
|
"test": "jest",
|
|
33
38
|
"test:watch": "jest --watch",
|
|
34
39
|
"test:coverage": "jest --coverage",
|
|
@@ -40,7 +45,6 @@
|
|
|
40
45
|
"example:clear": "npm --prefix examples/docusaurus-v3 run clear",
|
|
41
46
|
"prepare": "husky",
|
|
42
47
|
"prepublishOnly": "npm run build && npm test",
|
|
43
|
-
"version": "npm version",
|
|
44
48
|
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
|
|
45
49
|
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
|
|
46
50
|
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\" \"__tests__/**/*.js\"",
|
|
@@ -82,25 +86,28 @@
|
|
|
82
86
|
"@babel/preset-env": "^7.28.5",
|
|
83
87
|
"@babel/preset-react": "^7.28.5",
|
|
84
88
|
"@babel/preset-typescript": "^7.28.5",
|
|
85
|
-
"@eslint/js": "^9.28.0",
|
|
86
|
-
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
|
87
|
-
"@typescript-eslint/parser": "^8.33.0",
|
|
88
|
-
"eslint": "^9.28.0",
|
|
89
|
-
"eslint-plugin-react": "^7.37.5",
|
|
90
|
-
"eslint-plugin-react-hooks": "^5.2.0",
|
|
91
89
|
"@docusaurus/tsconfig": "^3.9.2",
|
|
92
90
|
"@docusaurus/types": "^3.9.2",
|
|
91
|
+
"@eslint/js": "^9.28.0",
|
|
93
92
|
"@playwright/test": "^1.56.1",
|
|
94
93
|
"@testing-library/jest-dom": "^6.9.1",
|
|
95
94
|
"@testing-library/react": "^16.3.0",
|
|
96
95
|
"@testing-library/user-event": "^14.6.1",
|
|
96
|
+
"@types/fs-extra": "^11.0.4",
|
|
97
|
+
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
|
98
|
+
"@typescript-eslint/parser": "^8.33.0",
|
|
97
99
|
"babel-jest": "^30.2.0",
|
|
100
|
+
"eslint": "^9.28.0",
|
|
101
|
+
"eslint-plugin-react": "^7.37.5",
|
|
102
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
103
|
+
"husky": "^9.1.7",
|
|
98
104
|
"identity-obj-proxy": "^3.0.0",
|
|
99
105
|
"jest": "^30.2.0",
|
|
100
106
|
"jest-environment-jsdom": "^30.2.0",
|
|
101
|
-
"husky": "^9.1.7",
|
|
102
107
|
"lint-staged": "^15.5.1",
|
|
103
108
|
"prettier": "^3.6.2",
|
|
109
|
+
"rimraf": "^6.0.1",
|
|
110
|
+
"tsup": "^8.0.1",
|
|
104
111
|
"typescript": "^5.7.3"
|
|
105
112
|
},
|
|
106
113
|
"lint-staged": {
|
|
@@ -111,17 +118,5 @@
|
|
|
111
118
|
"*.{json,css,md}": [
|
|
112
119
|
"prettier --write"
|
|
113
120
|
]
|
|
114
|
-
},
|
|
115
|
-
"browser": {
|
|
116
|
-
"path": false,
|
|
117
|
-
"url": false,
|
|
118
|
-
"fs": false,
|
|
119
|
-
"fs-extra": false,
|
|
120
|
-
"graceful-fs": false,
|
|
121
|
-
"jsonfile": false,
|
|
122
|
-
"util": false,
|
|
123
|
-
"assert": false,
|
|
124
|
-
"stream": false,
|
|
125
|
-
"constants": false
|
|
126
121
|
}
|
|
127
122
|
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import userEvent from '@testing-library/user-event';
|
|
4
|
-
import GlossaryPage from './GlossaryPage';
|
|
5
|
-
|
|
6
|
-
// Mock CSS modules
|
|
7
|
-
jest.mock('./GlossaryPage.module.css', () => ({
|
|
8
|
-
glossaryContainer: 'glossaryContainer',
|
|
9
|
-
glossaryHeader: 'glossaryHeader',
|
|
10
|
-
glossaryDescription: 'glossaryDescription',
|
|
11
|
-
searchContainer: 'searchContainer',
|
|
12
|
-
searchInput: 'searchInput',
|
|
13
|
-
noResults: 'noResults',
|
|
14
|
-
glossaryContent: 'glossaryContent',
|
|
15
|
-
letterNav: 'letterNav',
|
|
16
|
-
letterLink: 'letterLink',
|
|
17
|
-
letterSection: 'letterSection',
|
|
18
|
-
letterHeading: 'letterHeading',
|
|
19
|
-
termList: 'termList',
|
|
20
|
-
termItem: 'termItem',
|
|
21
|
-
termName: 'termName',
|
|
22
|
-
abbreviation: 'abbreviation',
|
|
23
|
-
termDefinition: 'termDefinition',
|
|
24
|
-
relatedTerms: 'relatedTerms',
|
|
25
|
-
glossaryFooter: 'glossaryFooter',
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
const mockGlossaryData = {
|
|
29
|
-
description: 'Test glossary',
|
|
30
|
-
terms: [
|
|
31
|
-
{
|
|
32
|
-
id: 'api',
|
|
33
|
-
term: 'API',
|
|
34
|
-
definition: 'Application Programming Interface',
|
|
35
|
-
abbreviation: 'API',
|
|
36
|
-
relatedTerms: ['REST'],
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: 'rest',
|
|
40
|
-
term: 'REST',
|
|
41
|
-
definition: 'Representational State Transfer',
|
|
42
|
-
abbreviation: 'REST',
|
|
43
|
-
relatedTerms: ['API'],
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: 'ml',
|
|
47
|
-
term: 'Machine Learning',
|
|
48
|
-
definition: 'A type of artificial intelligence',
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
describe('GlossaryPage', () => {
|
|
54
|
-
it('should render glossary with all terms', () => {
|
|
55
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
56
|
-
|
|
57
|
-
expect(screen.getByText('Glossary')).toBeInTheDocument();
|
|
58
|
-
expect(screen.getByText('Test glossary')).toBeInTheDocument();
|
|
59
|
-
// Use getAllBy since terms can appear multiple times (in term names and related links)
|
|
60
|
-
expect(screen.getAllByText('API').length).toBeGreaterThan(0);
|
|
61
|
-
expect(screen.getAllByText('REST').length).toBeGreaterThan(0);
|
|
62
|
-
expect(screen.getByText('Machine Learning')).toBeInTheDocument();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should show search input', () => {
|
|
66
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
67
|
-
|
|
68
|
-
const searchInput = screen.getByPlaceholderText('Search terms...');
|
|
69
|
-
expect(searchInput).toBeInTheDocument();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should filter terms by search query', async () => {
|
|
73
|
-
const user = userEvent.setup();
|
|
74
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
75
|
-
|
|
76
|
-
const searchInput = screen.getByPlaceholderText('Search terms...');
|
|
77
|
-
await user.type(searchInput, 'API');
|
|
78
|
-
|
|
79
|
-
expect(screen.getByText('API')).toBeInTheDocument();
|
|
80
|
-
expect(screen.queryByText('Machine Learning')).not.toBeInTheDocument();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should filter by definition text', async () => {
|
|
84
|
-
const user = userEvent.setup();
|
|
85
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
86
|
-
|
|
87
|
-
const searchInput = screen.getByPlaceholderText('Search terms...');
|
|
88
|
-
await user.type(searchInput, 'artificial');
|
|
89
|
-
|
|
90
|
-
expect(screen.getByText('Machine Learning')).toBeInTheDocument();
|
|
91
|
-
expect(screen.queryByText('API')).not.toBeInTheDocument();
|
|
92
|
-
expect(screen.queryByText('REST')).not.toBeInTheDocument();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should show no results message when no matches', async () => {
|
|
96
|
-
const user = userEvent.setup();
|
|
97
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
98
|
-
|
|
99
|
-
const searchInput = screen.getByPlaceholderText('Search terms...');
|
|
100
|
-
await user.type(searchInput, 'NonexistentTerm');
|
|
101
|
-
|
|
102
|
-
expect(screen.getByText(/No terms found matching/)).toBeInTheDocument();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should group terms by first letter', () => {
|
|
106
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
107
|
-
|
|
108
|
-
// Check that terms are grouped under their first letter (multiple elements per letter)
|
|
109
|
-
expect(screen.getAllByText('A').length).toBeGreaterThan(0);
|
|
110
|
-
expect(screen.getAllByText('M').length).toBeGreaterThan(0);
|
|
111
|
-
expect(screen.getAllByText('R').length).toBeGreaterThan(0);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should sort terms alphabetically within groups', () => {
|
|
115
|
-
const dataWithMultipleTerms = {
|
|
116
|
-
...mockGlossaryData,
|
|
117
|
-
terms: [
|
|
118
|
-
{ id: 'zebra', term: 'Zebra', definition: 'An animal' },
|
|
119
|
-
{ id: 'alpha', term: 'Alpha', definition: 'First letter' },
|
|
120
|
-
{ id: 'beta', term: 'Beta', definition: 'Second letter' },
|
|
121
|
-
],
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
render(<GlossaryPage glossaryData={dataWithMultipleTerms} />);
|
|
125
|
-
|
|
126
|
-
const terms = screen.getAllByText(/Zebra|Alpha|Beta/);
|
|
127
|
-
expect(terms).toHaveLength(3);
|
|
128
|
-
// Check Alpha is before Beta, and Beta is before Zebra
|
|
129
|
-
expect(terms[0]).toHaveTextContent('Alpha');
|
|
130
|
-
expect(terms[1]).toHaveTextContent('Beta');
|
|
131
|
-
expect(terms[2]).toHaveTextContent('Zebra');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should display abbreviations', () => {
|
|
135
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
136
|
-
|
|
137
|
-
// Check abbreviation appears in document
|
|
138
|
-
expect(screen.getAllByText('(API)').length).toBeGreaterThan(0);
|
|
139
|
-
expect(screen.getAllByText('(REST)').length).toBeGreaterThan(0);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should display related terms', () => {
|
|
143
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
144
|
-
|
|
145
|
-
// Related terms header appears multiple times (once per term with related terms)
|
|
146
|
-
expect(screen.getAllByText('Related terms:').length).toBeGreaterThan(0);
|
|
147
|
-
// REST appears as a term and in related links
|
|
148
|
-
expect(screen.getAllByText('REST').length).toBeGreaterThan(0);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should show total term count in footer', () => {
|
|
152
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
153
|
-
|
|
154
|
-
expect(screen.getByText('Total terms: 3')).toBeInTheDocument();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should handle empty terms array', () => {
|
|
158
|
-
const emptyData = { terms: [] };
|
|
159
|
-
render(<GlossaryPage glossaryData={emptyData} />);
|
|
160
|
-
|
|
161
|
-
expect(screen.getByText('Glossary')).toBeInTheDocument();
|
|
162
|
-
expect(screen.getByText('Total terms: 0')).toBeInTheDocument();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should handle missing glossaryData gracefully', () => {
|
|
166
|
-
render(<GlossaryPage glossaryData={null} />);
|
|
167
|
-
|
|
168
|
-
expect(screen.getByText('Glossary')).toBeInTheDocument();
|
|
169
|
-
expect(screen.getByText('Total terms: 0')).toBeInTheDocument();
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should handle missing description gracefully', () => {
|
|
173
|
-
const dataWithoutDescription = {
|
|
174
|
-
terms: mockGlossaryData.terms,
|
|
175
|
-
};
|
|
176
|
-
render(<GlossaryPage glossaryData={dataWithoutDescription} />);
|
|
177
|
-
|
|
178
|
-
expect(screen.getByText(/A collection of terms and their definitions/)).toBeInTheDocument();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should generate correct anchor links for terms', () => {
|
|
182
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
183
|
-
|
|
184
|
-
// Check navigation links are generated
|
|
185
|
-
const navLinks = screen.getAllByRole('link', { name: /^[AMR]$/ });
|
|
186
|
-
expect(navLinks.length).toBeGreaterThan(0);
|
|
187
|
-
expect(navLinks[0]).toHaveAttribute('href', '#letter-A');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should clear search when input is cleared', async () => {
|
|
191
|
-
const user = userEvent.setup();
|
|
192
|
-
render(<GlossaryPage glossaryData={mockGlossaryData} />);
|
|
193
|
-
|
|
194
|
-
const searchInput = screen.getByPlaceholderText('Search terms...');
|
|
195
|
-
await user.type(searchInput, 'Machine');
|
|
196
|
-
|
|
197
|
-
expect(screen.getByText('Machine Learning')).toBeInTheDocument();
|
|
198
|
-
|
|
199
|
-
await user.clear(searchInput);
|
|
200
|
-
|
|
201
|
-
expect(screen.getAllByText('API').length).toBeGreaterThan(0);
|
|
202
|
-
expect(screen.getAllByText('REST').length).toBeGreaterThan(0);
|
|
203
|
-
expect(screen.getByText('Machine Learning')).toBeInTheDocument();
|
|
204
|
-
});
|
|
205
|
-
});
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAU7D,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAgGR;AAGD,eAAO,MAAM,YAAY,4BAAsB,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGhE,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,qBAAqB,EACpC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,CAAC,OAAO,mBAAmB,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAa7F"}
|
package/dist/preset.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;;GAGG;AACH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC1C,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,KAAK,UAAU,GACX,KAAK,GACL,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACzB,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;CAC3B,CAAC,CAAC;AAEP;;GAEG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC3C,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3C;;GAEG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C;;;GAGG;AACH,KAAK,aAAa,GAAG,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,iFAAiF;IACjF,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAgGhG"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a remark plugin that automatically detects and replaces glossary terms in markdown
|
|
3
|
-
*
|
|
4
|
-
* This plugin transforms plain text terms into <GlossaryTerm> JSX elements.
|
|
5
|
-
* The GlossaryTerm component is globally available via the MDXComponents theme wrapper,
|
|
6
|
-
* so no import injection is needed - MDX files can use it without explicit imports.
|
|
7
|
-
*
|
|
8
|
-
* @param {object} options - Plugin options
|
|
9
|
-
* @param {Array} options.terms - Array of glossary term objects with {term, definition}
|
|
10
|
-
* @param {string} options.glossaryPath - Path to glossary JSON file (optional, if terms not provided)
|
|
11
|
-
* @param {string} options.routePath - Route path to glossary page (default: '/glossary')
|
|
12
|
-
* @param {string} options.siteDir - Docusaurus site directory (required if using glossaryPath)
|
|
13
|
-
* @returns {function} Remark plugin function
|
|
14
|
-
*/
|
|
15
|
-
export default function remarkGlossaryTerms({ terms, glossaryPath, routePath, siteDir, }?: {
|
|
16
|
-
terms: any[];
|
|
17
|
-
glossaryPath: string;
|
|
18
|
-
routePath: string;
|
|
19
|
-
siteDir: string;
|
|
20
|
-
}): Function;
|
|
21
|
-
/**
|
|
22
|
-
* Clears the glossary cache
|
|
23
|
-
* Useful for testing or when you want to force a reload of glossary data
|
|
24
|
-
*
|
|
25
|
-
* @param {string} [filePath] - Optional specific file path to clear. If not provided, clears entire cache.
|
|
26
|
-
*/
|
|
27
|
-
export function clearGlossaryCache(filePath?: string): void;
|
|
28
|
-
//# sourceMappingURL=glossary-terms.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"glossary-terms.d.ts","sourceRoot":"","sources":["../../src/remark/glossary-terms.js"],"names":[],"mappings":"AA+DA;;;;;;;;;;;;;GAaG;AACH,2FANG;IAAuB,KAAK;IACJ,YAAY,EAA5B,MAAM;IACU,SAAS,EAAzB,MAAM;IACU,OAAO,EAAvB,MAAM;CACd,YA8VF;AAED;;;;;GAKG;AACH,8CAFW,MAAM,QAQhB"}
|