docusaurus-plugin-glossary 1.2.0 → 1.3.2
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 +100 -30
- package/lib/index.js +246 -0
- package/{remark → lib/remark}/glossary-terms.js +23 -20
- package/{theme → lib/theme}/GlossaryTerm/styles.module.css +14 -12
- package/package.json +38 -9
- package/index.js +0 -156
- /package/{components → lib/components}/GlossaryPage.js +0 -0
- /package/{components → lib/components}/GlossaryPage.module.css +0 -0
- /package/{components → lib/components}/GlossaryPage.test.js +0 -0
- /package/{theme → lib/theme}/GlossaryTerm/index.js +0 -0
- /package/{theme → lib/theme}/GlossaryTerm/index.test.js +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive Docusaurus plugin that provides glossary functionality with an auto-generated glossary page, searchable terms, and inline term tooltips.
|
|
4
4
|
|
|
5
|
-
> Compatibility: Fully compatible with Docusaurus v3 (MDX v3). If you were on a v2-era fork, please upgrade to the latest 1.x release of this plugin.
|
|
5
|
+
> Compatibility: Fully compatible with Docusaurus v3 (MDX v3). If you were on a v2-era fork, please upgrade to the latest 1.x release of this plugin.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -26,15 +26,47 @@ A comprehensive Docusaurus plugin that provides glossary functionality with an a
|
|
|
26
26
|
2. **Add to your `docusaurus.config.js`:**
|
|
27
27
|
|
|
28
28
|
```javascript
|
|
29
|
+
const glossaryPlugin = require('docusaurus-plugin-glossary');
|
|
30
|
+
|
|
29
31
|
module.exports = {
|
|
30
32
|
// ... other config
|
|
33
|
+
presets: [
|
|
34
|
+
[
|
|
35
|
+
'@docusaurus/preset-classic',
|
|
36
|
+
{
|
|
37
|
+
docs: {
|
|
38
|
+
// Add the remark plugin to enable auto-linking in docs
|
|
39
|
+
remarkPlugins: [
|
|
40
|
+
glossaryPlugin.getRemarkPlugin(
|
|
41
|
+
{
|
|
42
|
+
glossaryPath: 'glossary/glossary.json',
|
|
43
|
+
routePath: '/glossary',
|
|
44
|
+
},
|
|
45
|
+
{ siteDir: __dirname }
|
|
46
|
+
),
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
pages: {
|
|
50
|
+
// Add the remark plugin to enable auto-linking in pages
|
|
51
|
+
remarkPlugins: [
|
|
52
|
+
glossaryPlugin.getRemarkPlugin(
|
|
53
|
+
{
|
|
54
|
+
glossaryPath: 'glossary/glossary.json',
|
|
55
|
+
routePath: '/glossary',
|
|
56
|
+
},
|
|
57
|
+
{ siteDir: __dirname }
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
],
|
|
31
64
|
plugins: [
|
|
32
65
|
[
|
|
33
66
|
'docusaurus-plugin-glossary',
|
|
34
67
|
{
|
|
35
68
|
glossaryPath: 'glossary/glossary.json', // Path to your glossary file
|
|
36
69
|
routePath: '/glossary', // URL path for glossary page
|
|
37
|
-
autoLinkTerms: true, // Automatically link terms (default: true)
|
|
38
70
|
},
|
|
39
71
|
],
|
|
40
72
|
],
|
|
@@ -42,7 +74,7 @@ A comprehensive Docusaurus plugin that provides glossary functionality with an a
|
|
|
42
74
|
};
|
|
43
75
|
```
|
|
44
76
|
|
|
45
|
-
**
|
|
77
|
+
**Note:** You need to configure the remark plugin in both the plugin AND the preset (for docs/pages) to enable auto-linking of terms.
|
|
46
78
|
|
|
47
79
|
3. **Create your glossary file at `glossary/glossary.json`:**
|
|
48
80
|
|
|
@@ -127,52 +159,90 @@ Create a JSON file at `glossary/glossary.json` (or your configured path) in your
|
|
|
127
159
|
Add the plugin to your `docusaurus.config.js`:
|
|
128
160
|
|
|
129
161
|
```javascript
|
|
162
|
+
const glossaryPlugin = require('docusaurus-plugin-glossary');
|
|
163
|
+
|
|
130
164
|
module.exports = {
|
|
165
|
+
presets: [
|
|
166
|
+
[
|
|
167
|
+
'@docusaurus/preset-classic',
|
|
168
|
+
{
|
|
169
|
+
docs: {
|
|
170
|
+
// Add the remark plugin to enable auto-linking in docs
|
|
171
|
+
remarkPlugins: [
|
|
172
|
+
glossaryPlugin.getRemarkPlugin(
|
|
173
|
+
{
|
|
174
|
+
glossaryPath: 'glossary/glossary.json',
|
|
175
|
+
routePath: '/glossary',
|
|
176
|
+
},
|
|
177
|
+
{ siteDir: __dirname }
|
|
178
|
+
),
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
pages: {
|
|
182
|
+
// Add the remark plugin to enable auto-linking in pages
|
|
183
|
+
remarkPlugins: [
|
|
184
|
+
glossaryPlugin.getRemarkPlugin(
|
|
185
|
+
{
|
|
186
|
+
glossaryPath: 'glossary/glossary.json',
|
|
187
|
+
routePath: '/glossary',
|
|
188
|
+
},
|
|
189
|
+
{ siteDir: __dirname }
|
|
190
|
+
),
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
],
|
|
131
196
|
plugins: [
|
|
132
197
|
[
|
|
133
198
|
'docusaurus-plugin-glossary',
|
|
134
199
|
{
|
|
135
200
|
glossaryPath: 'glossary/glossary.json', // Path to your glossary file
|
|
136
201
|
routePath: '/glossary', // URL path for the glossary page
|
|
137
|
-
autoLinkTerms: true, // Automatically detect and link terms (default: true)
|
|
138
202
|
},
|
|
139
203
|
],
|
|
140
204
|
],
|
|
141
205
|
};
|
|
142
206
|
```
|
|
143
207
|
|
|
144
|
-
**
|
|
208
|
+
**Required Configuration:** To enable automatic term detection and linking, you must configure the remark plugin in your preset (as shown above). The plugin provides a `getRemarkPlugin` helper to make this easier.
|
|
145
209
|
|
|
146
|
-
**
|
|
210
|
+
**Alternative: Using remarkPlugin directly**
|
|
147
211
|
|
|
148
|
-
|
|
212
|
+
You can also use the `remarkPlugin` export directly if you prefer:
|
|
149
213
|
|
|
150
214
|
```javascript
|
|
151
215
|
const glossaryPlugin = require('docusaurus-plugin-glossary');
|
|
152
216
|
|
|
153
217
|
module.exports = {
|
|
218
|
+
presets: [
|
|
219
|
+
[
|
|
220
|
+
'@docusaurus/preset-classic',
|
|
221
|
+
{
|
|
222
|
+
docs: {
|
|
223
|
+
remarkPlugins: [
|
|
224
|
+
[
|
|
225
|
+
glossaryPlugin.remarkPlugin,
|
|
226
|
+
{
|
|
227
|
+
glossaryPath: 'glossary/glossary.json',
|
|
228
|
+
routePath: '/glossary',
|
|
229
|
+
siteDir: __dirname,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
],
|
|
154
237
|
plugins: [
|
|
155
238
|
[
|
|
156
239
|
'docusaurus-plugin-glossary',
|
|
157
240
|
{
|
|
158
241
|
glossaryPath: 'glossary/glossary.json',
|
|
159
242
|
routePath: '/glossary',
|
|
160
|
-
autoLinkTerms: false, // Disable automatic configuration
|
|
161
243
|
},
|
|
162
244
|
],
|
|
163
245
|
],
|
|
164
|
-
markdown: {
|
|
165
|
-
remarkPlugins: [
|
|
166
|
-
[
|
|
167
|
-
glossaryPlugin.remarkPlugin,
|
|
168
|
-
{
|
|
169
|
-
glossaryPath: 'glossary/glossary.json',
|
|
170
|
-
routePath: '/glossary',
|
|
171
|
-
siteDir: process.cwd(),
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
],
|
|
175
|
-
},
|
|
176
246
|
};
|
|
177
247
|
```
|
|
178
248
|
|
|
@@ -187,12 +257,13 @@ module.exports = {
|
|
|
187
257
|
```
|
|
188
258
|
|
|
189
259
|
- **No tooltips or no auto-linking?**
|
|
190
|
-
- Confirm you
|
|
191
|
-
- Ensure the plugin is listed in `plugins`
|
|
260
|
+
- Confirm you're on `@docusaurus/core@^3` and `react@^18`.
|
|
261
|
+
- Ensure the plugin is listed in `plugins` AND the remark plugin is configured in your preset (see Step 2 above).
|
|
192
262
|
- Visit `/glossary`. If the page or route fails to render, verify your `glossaryPath` file exists and contains a `terms` array.
|
|
263
|
+
- Clear your Docusaurus cache with `npm run clear` and restart your dev server.
|
|
193
264
|
- If you previously used a local patch for `1.0.0`, remove it when using `1.0.2+`; the plugin bundles the v3-compatible theme and remark integration.
|
|
194
265
|
|
|
195
|
-
- **Opting out of auto-linking**:
|
|
266
|
+
- **Opting out of auto-linking**: Simply don't configure the remark plugin in your preset. You can still use the `<GlossaryTerm />` component manually where you want explicit control.
|
|
196
267
|
|
|
197
268
|
### Step 3: Use Glossary Terms in Your Content
|
|
198
269
|
|
|
@@ -272,11 +343,10 @@ module.exports = {
|
|
|
272
343
|
|
|
273
344
|
## Configuration Options
|
|
274
345
|
|
|
275
|
-
| Option
|
|
276
|
-
|
|
|
277
|
-
| `glossaryPath`
|
|
278
|
-
| `routePath`
|
|
279
|
-
| `autoLinkTerms` | boolean | `true` | Enable automatic term detection in markdown (requires remark plugin configuration) |
|
|
346
|
+
| Option | Type | Default | Description |
|
|
347
|
+
| -------------- | ------ | -------------------------- | ----------------------------------------------------- |
|
|
348
|
+
| `glossaryPath` | string | `'glossary/glossary.json'` | Path to glossary JSON file relative to site directory |
|
|
349
|
+
| `routePath` | string | `'/glossary'` | URL path for glossary page |
|
|
280
350
|
|
|
281
351
|
## Customization
|
|
282
352
|
|
|
@@ -439,12 +509,12 @@ The remark plugin (`remark/glossary-terms.js`) automatically detects glossary te
|
|
|
439
509
|
|
|
440
510
|
### Automatic term detection not working
|
|
441
511
|
|
|
442
|
-
- Ensure
|
|
443
|
-
- The remark plugin is automatically configured, so you don't need to manually add it to `markdown.remarkPlugins`
|
|
512
|
+
- **IMPORTANT**: Ensure you have configured the remark plugin in your preset (see Configuration section above)
|
|
444
513
|
- Verify your glossary file exists at the configured `glossaryPath` and contains terms
|
|
445
514
|
- Check that terms in your content match the terms in your glossary (matching is case-insensitive but respects word boundaries)
|
|
446
515
|
- Try clearing cache with `npm run clear` and restarting the dev server
|
|
447
516
|
- Note that terms inside code blocks, links, or MDX components are not processed
|
|
517
|
+
- Make sure you've added the remark plugin to both `docs` and `pages` sections if you want auto-linking in both
|
|
448
518
|
- If you've manually configured the remark plugin, ensure `siteDir` points to the correct Docusaurus site directory
|
|
449
519
|
|
|
450
520
|
### Styles not applying
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
var _a, _b;
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import validatePeerDependencies from 'validate-peer-dependencies';
|
|
6
|
+
import remarkGlossaryTerms from './remark/glossary-terms.js';
|
|
7
|
+
// Helper function to compute __dirname lazily when needed
|
|
8
|
+
// This avoids webpack bundling issues by not using fileURLToPath at module load time
|
|
9
|
+
function getDirname() {
|
|
10
|
+
var _a;
|
|
11
|
+
// Check if we're in a Node.js environment
|
|
12
|
+
if (typeof process === 'undefined' || !((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node)) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
// Use cached value if available
|
|
16
|
+
if (globalThis.__dirnameCache) {
|
|
17
|
+
return globalThis.__dirnameCache;
|
|
18
|
+
}
|
|
19
|
+
// In Jest/Babel transformed environment, __filename is available
|
|
20
|
+
// @ts-ignore - __filename is available after Babel transforms ES modules to CommonJS
|
|
21
|
+
if (typeof __filename !== 'undefined') {
|
|
22
|
+
const computedDirname = path.dirname(__filename);
|
|
23
|
+
globalThis.__dirnameCache = computedDirname;
|
|
24
|
+
return computedDirname;
|
|
25
|
+
}
|
|
26
|
+
// Check if import.meta.url is available
|
|
27
|
+
// Use try-catch to handle cases where import.meta is undefined (e.g., in Jest before transform)
|
|
28
|
+
let hasImportMetaUrl = false;
|
|
29
|
+
try {
|
|
30
|
+
hasImportMetaUrl = typeof import.meta.url !== 'undefined';
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// import.meta is undefined (e.g., in Jest environment before Babel transform)
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
if (!hasImportMetaUrl) {
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
// Try to compute __dirname using fileURLToPath via createRequire
|
|
40
|
+
// This avoids webpack trying to bundle fileURLToPath at module load time
|
|
41
|
+
try {
|
|
42
|
+
const require = createRequire(import.meta.url);
|
|
43
|
+
const urlModule = require('url');
|
|
44
|
+
// Check if fileURLToPath is actually a function (webpack may provide a broken polyfill)
|
|
45
|
+
if (urlModule && typeof urlModule.fileURLToPath === 'function') {
|
|
46
|
+
const __filename = urlModule.fileURLToPath(import.meta.url);
|
|
47
|
+
const computedDirname = path.dirname(__filename);
|
|
48
|
+
globalThis.__dirnameCache = computedDirname;
|
|
49
|
+
return computedDirname;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// If webpack provides a broken polyfill or require fails, return empty
|
|
54
|
+
// __dirname will be computed when the plugin function is called (server-side only)
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
// Initialize __dirname at module load time, but handle webpack bundling gracefully
|
|
60
|
+
let __dirname = '';
|
|
61
|
+
let peerDepsValidated = false;
|
|
62
|
+
try {
|
|
63
|
+
// Only compute __dirname if we're in Node.js (not during webpack bundling)
|
|
64
|
+
if (typeof process !== 'undefined' && ((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node)) {
|
|
65
|
+
// In Jest/Babel transformed environment, __filename is available
|
|
66
|
+
// @ts-ignore - __filename is available after Babel transforms ES modules to CommonJS
|
|
67
|
+
if (typeof __filename !== 'undefined') {
|
|
68
|
+
__dirname = path.dirname(__filename);
|
|
69
|
+
validatePeerDependencies(__dirname);
|
|
70
|
+
peerDepsValidated = true;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Check if import.meta.url is available - use try-catch since import.meta might be undefined
|
|
74
|
+
let hasImportMetaUrl = false;
|
|
75
|
+
try {
|
|
76
|
+
hasImportMetaUrl = typeof import.meta.url !== 'undefined';
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// import.meta is undefined (e.g., in Jest environment before Babel transform)
|
|
80
|
+
hasImportMetaUrl = false;
|
|
81
|
+
}
|
|
82
|
+
if (hasImportMetaUrl) {
|
|
83
|
+
const require = createRequire(import.meta.url);
|
|
84
|
+
const urlModule = require('url');
|
|
85
|
+
// Check if fileURLToPath is actually a function (not a webpack polyfill)
|
|
86
|
+
if (urlModule && typeof urlModule.fileURLToPath === 'function') {
|
|
87
|
+
const __filename = urlModule.fileURLToPath(import.meta.url);
|
|
88
|
+
__dirname = path.dirname(__filename);
|
|
89
|
+
validatePeerDependencies(__dirname);
|
|
90
|
+
peerDepsValidated = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// If initialization fails (e.g., during webpack bundling), __dirname will be empty
|
|
98
|
+
// and will be computed lazily via getDirname() when needed
|
|
99
|
+
}
|
|
100
|
+
// Validate peer dependencies lazily if not already validated
|
|
101
|
+
if (!peerDepsValidated && typeof process !== 'undefined' && ((_b = process.versions) === null || _b === void 0 ? void 0 : _b.node)) {
|
|
102
|
+
try {
|
|
103
|
+
const dirname = getDirname();
|
|
104
|
+
if (dirname) {
|
|
105
|
+
validatePeerDependencies(dirname);
|
|
106
|
+
peerDepsValidated = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Ignore validation errors during webpack bundling
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Docusaurus Glossary Plugin
|
|
115
|
+
*
|
|
116
|
+
* A plugin that provides glossary functionality with:
|
|
117
|
+
* - Glossary terms defined in a JSON file
|
|
118
|
+
* - Auto-generated glossary page
|
|
119
|
+
* - GlossaryTerm component for inline definitions
|
|
120
|
+
* - Tooltips on hover
|
|
121
|
+
* - Automatic glossary term detection in markdown files (requires manual remark plugin configuration)
|
|
122
|
+
*
|
|
123
|
+
* IMPORTANT: To enable auto-linking of glossary terms, you must manually add the remark plugin
|
|
124
|
+
* to your docusaurus.config.js using the getRemarkPlugin helper:
|
|
125
|
+
*
|
|
126
|
+
* Example:
|
|
127
|
+
* ```javascript
|
|
128
|
+
* const glossaryPlugin = require('docusaurus-plugin-glossary');
|
|
129
|
+
*
|
|
130
|
+
* module.exports = {
|
|
131
|
+
* presets: [
|
|
132
|
+
* ['@docusaurus/preset-classic', {
|
|
133
|
+
* docs: {
|
|
134
|
+
* remarkPlugins: [
|
|
135
|
+
* glossaryPlugin.getRemarkPlugin({
|
|
136
|
+
* glossaryPath: 'glossary/glossary.json',
|
|
137
|
+
* routePath: '/glossary',
|
|
138
|
+
* }, { siteDir: __dirname }),
|
|
139
|
+
* ],
|
|
140
|
+
* },
|
|
141
|
+
* pages: {
|
|
142
|
+
* remarkPlugins: [
|
|
143
|
+
* glossaryPlugin.getRemarkPlugin({
|
|
144
|
+
* glossaryPath: 'glossary/glossary.json',
|
|
145
|
+
* routePath: '/glossary',
|
|
146
|
+
* }, { siteDir: __dirname }),
|
|
147
|
+
* ],
|
|
148
|
+
* },
|
|
149
|
+
* }],
|
|
150
|
+
* ],
|
|
151
|
+
* plugins: [
|
|
152
|
+
* ['docusaurus-plugin-glossary', {
|
|
153
|
+
* glossaryPath: 'glossary/glossary.json',
|
|
154
|
+
* routePath: '/glossary',
|
|
155
|
+
* }],
|
|
156
|
+
* ],
|
|
157
|
+
* };
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @param context - Docusaurus context
|
|
161
|
+
* @param options - Plugin options
|
|
162
|
+
* @param options.glossaryPath - Path to glossary JSON file (default: 'glossary/glossary.json')
|
|
163
|
+
* @param options.routePath - Route path for glossary page (default: '/glossary')
|
|
164
|
+
* @param options.autoLinkTerms - Legacy option, kept for compatibility but no longer used (configure remark plugin manually instead)
|
|
165
|
+
* @returns Plugin object
|
|
166
|
+
*/
|
|
167
|
+
export default function glossaryPlugin(context, options = {}) {
|
|
168
|
+
const { glossaryPath = 'glossary/glossary.json', routePath = '/glossary', autoLinkTerms = true, } = options;
|
|
169
|
+
let glossaryDataCache = { terms: [] };
|
|
170
|
+
return {
|
|
171
|
+
name: 'docusaurus-plugin-glossary',
|
|
172
|
+
async loadContent() {
|
|
173
|
+
// Load glossary terms from JSON file
|
|
174
|
+
const glossaryFilePath = path.resolve(context.siteDir, glossaryPath);
|
|
175
|
+
if (await fs.pathExists(glossaryFilePath)) {
|
|
176
|
+
const glossaryData = (await fs.readJson(glossaryFilePath));
|
|
177
|
+
glossaryDataCache = glossaryData;
|
|
178
|
+
return glossaryData;
|
|
179
|
+
}
|
|
180
|
+
console.warn(`Glossary file not found at ${glossaryFilePath}. Using empty glossary.`);
|
|
181
|
+
glossaryDataCache = { terms: [] };
|
|
182
|
+
return { terms: [] };
|
|
183
|
+
},
|
|
184
|
+
async contentLoaded({ content, actions }) {
|
|
185
|
+
const { createData, addRoute, setGlobalData } = actions;
|
|
186
|
+
const glossaryContent = content;
|
|
187
|
+
// Create data file that can be imported by components
|
|
188
|
+
const glossaryDataPath = await createData('glossary-data.json', JSON.stringify(glossaryContent));
|
|
189
|
+
// Create a data file for the remark plugin to access glossary terms
|
|
190
|
+
const remarkGlossaryDataPath = await createData('remark-glossary-data.json', JSON.stringify({
|
|
191
|
+
terms: glossaryContent.terms || [],
|
|
192
|
+
routePath: routePath,
|
|
193
|
+
}));
|
|
194
|
+
// Add glossary page route
|
|
195
|
+
// Compute __dirname if not already set (for webpack bundling compatibility)
|
|
196
|
+
const pluginDirname = __dirname || getDirname();
|
|
197
|
+
addRoute({
|
|
198
|
+
path: routePath,
|
|
199
|
+
component: path.join(pluginDirname, 'components/GlossaryPage.js'),
|
|
200
|
+
exact: true,
|
|
201
|
+
modules: {
|
|
202
|
+
glossaryData: glossaryDataPath,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
// Expose global data for runtime lookups (used by GlossaryTerm)
|
|
206
|
+
setGlobalData({
|
|
207
|
+
terms: glossaryContent.terms || [],
|
|
208
|
+
routePath,
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
getThemePath() {
|
|
212
|
+
// Compute __dirname if not already set (for webpack bundling compatibility)
|
|
213
|
+
const pluginDirname = __dirname || getDirname();
|
|
214
|
+
return path.resolve(pluginDirname, './theme');
|
|
215
|
+
},
|
|
216
|
+
getPathsToWatch() {
|
|
217
|
+
return [path.resolve(context.siteDir, glossaryPath)];
|
|
218
|
+
},
|
|
219
|
+
async postBuild({ outDir }) {
|
|
220
|
+
// You can add any post-build steps here if needed
|
|
221
|
+
console.log('Glossary plugin: Build completed');
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// Export remark plugin factory for use in markdown configuration
|
|
226
|
+
export const remarkPlugin = remarkGlossaryTerms;
|
|
227
|
+
/**
|
|
228
|
+
* Helper function to get the configured remark plugin
|
|
229
|
+
* This can be used in docusaurus.config.js markdown configuration
|
|
230
|
+
*
|
|
231
|
+
* @param pluginOptions - Plugin options from docusaurus.config.js
|
|
232
|
+
* @param context - Context with siteDir
|
|
233
|
+
* @returns Configured remark plugin
|
|
234
|
+
*/
|
|
235
|
+
export function getRemarkPlugin(pluginOptions, context) {
|
|
236
|
+
const { glossaryPath = 'glossary/glossary.json', routePath = '/glossary' } = pluginOptions;
|
|
237
|
+
const siteDir = context === null || context === void 0 ? void 0 : context.siteDir;
|
|
238
|
+
return [
|
|
239
|
+
remarkGlossaryTerms,
|
|
240
|
+
{
|
|
241
|
+
glossaryPath,
|
|
242
|
+
routePath,
|
|
243
|
+
siteDir,
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const fs = require('fs');
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Creates a remark plugin that automatically detects and replaces glossary terms in markdown
|
|
@@ -14,7 +12,7 @@ const fs = require('fs');
|
|
|
14
12
|
* @param {string} options.siteDir - Docusaurus site directory (required if using glossaryPath)
|
|
15
13
|
* @returns {function} Remark plugin function
|
|
16
14
|
*/
|
|
17
|
-
function remarkGlossaryTerms({
|
|
15
|
+
export default function remarkGlossaryTerms({
|
|
18
16
|
terms = [],
|
|
19
17
|
glossaryPath = null,
|
|
20
18
|
routePath = '/glossary',
|
|
@@ -79,10 +77,8 @@ function remarkGlossaryTerms({
|
|
|
79
77
|
// Check if it's a whole word match, with simple plural tolerance ('s' or 'es')
|
|
80
78
|
const beforeChar = index > 0 ? textLower[index - 1] : ' ';
|
|
81
79
|
const afterIndex = index + lowerTerm.length;
|
|
82
|
-
const afterChar = afterIndex < textLower.length
|
|
83
|
-
|
|
84
|
-
: ' ';
|
|
85
|
-
|
|
80
|
+
const afterChar = afterIndex < textLower.length ? textLower[afterIndex] : ' ';
|
|
81
|
+
|
|
86
82
|
let matchLength = term.length;
|
|
87
83
|
let isWordBoundary = !/\w/.test(beforeChar) && !/\w/.test(afterChar);
|
|
88
84
|
|
|
@@ -96,7 +92,12 @@ function remarkGlossaryTerms({
|
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
// Allow trailing 'es' plural (e.g., API -> APIs, box -> boxes)
|
|
99
|
-
if (
|
|
95
|
+
if (
|
|
96
|
+
!isWordBoundary &&
|
|
97
|
+
afterChar === 'e' &&
|
|
98
|
+
afterIndex + 1 < textLower.length &&
|
|
99
|
+
textLower[afterIndex + 1] === 's'
|
|
100
|
+
) {
|
|
100
101
|
const nextChar = afterIndex + 2 < textLower.length ? textLower[afterIndex + 2] : ' ';
|
|
101
102
|
if (!/\w/.test(nextChar)) {
|
|
102
103
|
isWordBoundary = true;
|
|
@@ -111,7 +112,7 @@ function remarkGlossaryTerms({
|
|
|
111
112
|
term: term,
|
|
112
113
|
termObj: termObj,
|
|
113
114
|
// Store original case from the text
|
|
114
|
-
originalText: text.substring(index, index + matchLength)
|
|
115
|
+
originalText: text.substring(index, index + matchLength),
|
|
115
116
|
});
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -237,10 +238,11 @@ function remarkGlossaryTerms({
|
|
|
237
238
|
|
|
238
239
|
// Inject MDX import for GlossaryTerm if we used it anywhere in this file
|
|
239
240
|
if (usedGlossaryTerm) {
|
|
240
|
-
// Create import node matching MDX expectations
|
|
241
|
+
// Create import node matching MDX v3 expectations
|
|
242
|
+
// Both 'value' (the import string) and 'data.estree' (the parsed AST) are required
|
|
241
243
|
const importNode = {
|
|
242
244
|
type: 'mdxjsEsm',
|
|
243
|
-
value: '',
|
|
245
|
+
value: "import GlossaryTerm from '@theme/GlossaryTerm'",
|
|
244
246
|
data: {
|
|
245
247
|
estree: {
|
|
246
248
|
type: 'Program',
|
|
@@ -254,7 +256,11 @@ function remarkGlossaryTerms({
|
|
|
254
256
|
local: { type: 'Identifier', name: 'GlossaryTerm' },
|
|
255
257
|
},
|
|
256
258
|
],
|
|
257
|
-
source: {
|
|
259
|
+
source: {
|
|
260
|
+
type: 'Literal',
|
|
261
|
+
value: '@theme/GlossaryTerm',
|
|
262
|
+
raw: "'@theme/GlossaryTerm'",
|
|
263
|
+
},
|
|
258
264
|
},
|
|
259
265
|
],
|
|
260
266
|
},
|
|
@@ -274,13 +280,12 @@ function remarkGlossaryTerms({
|
|
|
274
280
|
if (n.data?.estree?.body) {
|
|
275
281
|
return n.data.estree.body.some(
|
|
276
282
|
stmt =>
|
|
277
|
-
stmt.type === 'ImportDeclaration' &&
|
|
278
|
-
stmt.source?.value === '@theme/GlossaryTerm'
|
|
283
|
+
stmt.type === 'ImportDeclaration' && stmt.source?.value === '@theme/GlossaryTerm'
|
|
279
284
|
);
|
|
280
285
|
}
|
|
281
286
|
return false;
|
|
282
287
|
});
|
|
283
|
-
|
|
288
|
+
|
|
284
289
|
if (!hasExistingImport) {
|
|
285
290
|
// Place import at the very beginning of the file (before all other nodes)
|
|
286
291
|
// This ensures it's available when MDX compiles the JSX elements
|
|
@@ -297,5 +302,3 @@ function remarkGlossaryTerms({
|
|
|
297
302
|
}
|
|
298
303
|
};
|
|
299
304
|
}
|
|
300
|
-
|
|
301
|
-
module.exports = remarkGlossaryTerms;
|
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
.glossaryTerm {
|
|
7
|
-
color: var(--ifm-color-
|
|
7
|
+
color: var(--ifm-font-color-base);
|
|
8
8
|
text-decoration: none;
|
|
9
|
-
border-bottom: 1px dotted
|
|
9
|
+
border-bottom: 1px dotted;
|
|
10
|
+
border-bottom-color: color-mix(in srgb, var(--ifm-font-color-base) 20%, transparent);
|
|
10
11
|
cursor: help;
|
|
11
|
-
font-weight: 500;
|
|
12
12
|
transition: all 0.2s;
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
14
|
|
|
15
15
|
.glossaryTerm:hover {
|
|
16
16
|
color: var(--ifm-color-primary-dark);
|
|
17
17
|
border-bottom-style: solid;
|
|
18
|
+
border-bottom-color: var(--ifm-color-primary-dark);
|
|
19
|
+
border-bottom: none;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
.tooltip {
|
|
@@ -59,7 +61,7 @@
|
|
|
59
61
|
transform: translateX(-50%);
|
|
60
62
|
bottom: -6px;
|
|
61
63
|
border: 6px solid transparent;
|
|
62
|
-
border-
|
|
64
|
+
border-top-color: var(--ifm-background-surface-color);
|
|
63
65
|
}
|
|
64
66
|
.tooltipTop::before {
|
|
65
67
|
content: '';
|
|
@@ -68,7 +70,7 @@
|
|
|
68
70
|
transform: translateX(-50%);
|
|
69
71
|
bottom: -7px;
|
|
70
72
|
border: 7px solid transparent;
|
|
71
|
-
border-
|
|
73
|
+
border-top-color: var(--ifm-color-emphasis-300);
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
/* Arrow for bottom placement (tooltip below anchor) */
|
|
@@ -79,7 +81,7 @@
|
|
|
79
81
|
transform: translateX(-50%);
|
|
80
82
|
top: -6px;
|
|
81
83
|
border: 6px solid transparent;
|
|
82
|
-
border-
|
|
84
|
+
border-bottom-color: var(--ifm-background-surface-color);
|
|
83
85
|
}
|
|
84
86
|
.tooltipBottom::before {
|
|
85
87
|
content: '';
|
|
@@ -88,7 +90,7 @@
|
|
|
88
90
|
transform: translateX(-50%);
|
|
89
91
|
top: -7px;
|
|
90
92
|
border: 7px solid transparent;
|
|
91
|
-
border-
|
|
93
|
+
border-bottom-color: var(--ifm-color-emphasis-300);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
.tooltipVisible {
|
|
@@ -111,16 +113,16 @@
|
|
|
111
113
|
|
|
112
114
|
/* Dark mode arrow overrides scoped to placement to avoid upside-down arrows */
|
|
113
115
|
[data-theme='dark'] .tooltipTop::after {
|
|
114
|
-
border-
|
|
116
|
+
border-top-color: var(--ifm-background-surface-color);
|
|
115
117
|
}
|
|
116
118
|
[data-theme='dark'] .tooltipTop::before {
|
|
117
|
-
border-
|
|
119
|
+
border-top-color: var(--ifm-color-emphasis-400);
|
|
118
120
|
}
|
|
119
121
|
[data-theme='dark'] .tooltipBottom::after {
|
|
120
|
-
border-
|
|
122
|
+
border-bottom-color: var(--ifm-background-surface-color);
|
|
121
123
|
}
|
|
122
124
|
[data-theme='dark'] .tooltipBottom::before {
|
|
123
|
-
border-
|
|
125
|
+
border-bottom-color: var(--ifm-color-emphasis-400);
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
/* Responsive adjustments */
|
package/package.json
CHANGED
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docusaurus-plugin-glossary",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.3.2",
|
|
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": "lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./lib/index.js",
|
|
10
|
+
"require": "./lib/index.js",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./remark/glossary-terms": {
|
|
14
|
+
"import": "./lib/remark/glossary-terms.js",
|
|
15
|
+
"require": "./lib/remark/glossary-terms.js",
|
|
16
|
+
"default": "./lib/remark/glossary-terms.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
6
19
|
"files": [
|
|
7
|
-
"
|
|
8
|
-
"components/",
|
|
9
|
-
"theme/",
|
|
10
|
-
"remark/",
|
|
20
|
+
"lib/",
|
|
11
21
|
"README.md",
|
|
12
22
|
"LICENSE"
|
|
13
23
|
],
|
|
14
24
|
"scripts": {
|
|
25
|
+
"build": "tsc && node scripts/build.js",
|
|
26
|
+
"watch": "node scripts/watch.js",
|
|
15
27
|
"test": "jest",
|
|
16
28
|
"test:watch": "jest --watch",
|
|
17
29
|
"test:coverage": "jest --coverage",
|
|
18
30
|
"example:start": "npm --prefix examples/docusaurus-v3 run start",
|
|
19
31
|
"example:build": "npm --prefix examples/docusaurus-v3 run build",
|
|
20
32
|
"example:serve": "npm --prefix examples/docusaurus-v3 run serve",
|
|
21
|
-
"
|
|
33
|
+
"example:clear": "npm --prefix examples/docusaurus-v3 run clear",
|
|
34
|
+
"prepublishOnly": "npm run build && npm test",
|
|
22
35
|
"version": "npm version",
|
|
23
36
|
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
|
|
24
37
|
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\""
|
|
@@ -49,7 +62,8 @@
|
|
|
49
62
|
},
|
|
50
63
|
"dependencies": {
|
|
51
64
|
"fs-extra": "^11.0.0",
|
|
52
|
-
"unist-util-visit": "^5.0.0"
|
|
65
|
+
"unist-util-visit": "^5.0.0",
|
|
66
|
+
"validate-peer-dependencies": "^2.2.0"
|
|
53
67
|
},
|
|
54
68
|
"engines": {
|
|
55
69
|
"node": ">=16.14"
|
|
@@ -57,6 +71,8 @@
|
|
|
57
71
|
"devDependencies": {
|
|
58
72
|
"@babel/preset-env": "^7.28.5",
|
|
59
73
|
"@babel/preset-react": "^7.28.5",
|
|
74
|
+
"@docusaurus/tsconfig": "^3.9.2",
|
|
75
|
+
"@docusaurus/types": "^3.9.2",
|
|
60
76
|
"@testing-library/jest-dom": "^6.9.1",
|
|
61
77
|
"@testing-library/react": "^16.3.0",
|
|
62
78
|
"@testing-library/user-event": "^14.6.1",
|
|
@@ -64,6 +80,19 @@
|
|
|
64
80
|
"identity-obj-proxy": "^3.0.0",
|
|
65
81
|
"jest": "^30.2.0",
|
|
66
82
|
"jest-environment-jsdom": "^30.2.0",
|
|
67
|
-
"prettier": "^3.6.2"
|
|
83
|
+
"prettier": "^3.6.2",
|
|
84
|
+
"typescript": "^5.7.3"
|
|
85
|
+
},
|
|
86
|
+
"browser": {
|
|
87
|
+
"path": false,
|
|
88
|
+
"url": false,
|
|
89
|
+
"fs": false,
|
|
90
|
+
"fs-extra": false,
|
|
91
|
+
"graceful-fs": false,
|
|
92
|
+
"jsonfile": false,
|
|
93
|
+
"util": false,
|
|
94
|
+
"assert": false,
|
|
95
|
+
"stream": false,
|
|
96
|
+
"constants": false
|
|
68
97
|
}
|
|
69
98
|
}
|
package/index.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Docusaurus Glossary Plugin
|
|
6
|
-
*
|
|
7
|
-
* A plugin that provides glossary functionality with:
|
|
8
|
-
* - Glossary terms defined in a JSON file
|
|
9
|
-
* - Auto-generated glossary page
|
|
10
|
-
* - GlossaryTerm component for inline definitions
|
|
11
|
-
* - Tooltips on hover
|
|
12
|
-
* - Automatic glossary term detection in markdown files
|
|
13
|
-
*
|
|
14
|
-
* @param {object} context - Docusaurus context
|
|
15
|
-
* @param {object} options - Plugin options
|
|
16
|
-
* @param {string} options.glossaryPath - Path to glossary JSON file (default: 'glossary/glossary.json')
|
|
17
|
-
* @param {string} options.routePath - Route path for glossary page (default: '/glossary')
|
|
18
|
-
* @param {boolean} options.autoLinkTerms - Automatically link glossary terms in markdown (default: true)
|
|
19
|
-
* @returns {object} Plugin object
|
|
20
|
-
*/
|
|
21
|
-
function glossaryPlugin(context, options = {}) {
|
|
22
|
-
const {
|
|
23
|
-
glossaryPath = 'glossary/glossary.json',
|
|
24
|
-
routePath = '/glossary',
|
|
25
|
-
autoLinkTerms = true,
|
|
26
|
-
} = options;
|
|
27
|
-
|
|
28
|
-
let glossaryDataCache = { terms: [] };
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
name: 'docusaurus-plugin-glossary',
|
|
32
|
-
|
|
33
|
-
configureMarkdown(markdownConfig) {
|
|
34
|
-
// Automatically configure the remark plugin if autoLinkTerms is enabled
|
|
35
|
-
if (autoLinkTerms) {
|
|
36
|
-
if (!markdownConfig.remarkPlugins) {
|
|
37
|
-
markdownConfig.remarkPlugins = [];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const remarkPlugin = require('./remark/glossary-terms');
|
|
41
|
-
|
|
42
|
-
// Check if the remark plugin is already configured
|
|
43
|
-
const isAlreadyConfigured = markdownConfig.remarkPlugins.some(plugin => {
|
|
44
|
-
if (Array.isArray(plugin) && plugin[0]) {
|
|
45
|
-
// Check if it's our remark plugin by comparing the function reference
|
|
46
|
-
return plugin[0] === remarkPlugin;
|
|
47
|
-
}
|
|
48
|
-
// Also check if it's directly the remark plugin function
|
|
49
|
-
return plugin === remarkPlugin;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Only add if not already configured
|
|
53
|
-
if (!isAlreadyConfigured) {
|
|
54
|
-
markdownConfig.remarkPlugins.push([
|
|
55
|
-
remarkPlugin,
|
|
56
|
-
{
|
|
57
|
-
glossaryPath,
|
|
58
|
-
routePath,
|
|
59
|
-
siteDir: context.siteDir,
|
|
60
|
-
},
|
|
61
|
-
]);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
async loadContent() {
|
|
67
|
-
// Load glossary terms from JSON file
|
|
68
|
-
const glossaryFilePath = path.resolve(context.siteDir, glossaryPath);
|
|
69
|
-
|
|
70
|
-
if (await fs.pathExists(glossaryFilePath)) {
|
|
71
|
-
const glossaryData = await fs.readJson(glossaryFilePath);
|
|
72
|
-
glossaryDataCache = glossaryData;
|
|
73
|
-
return glossaryData;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
console.warn(`Glossary file not found at ${glossaryFilePath}. Using empty glossary.`);
|
|
77
|
-
glossaryDataCache = { terms: [] };
|
|
78
|
-
return { terms: [] };
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
async contentLoaded({ content, actions }) {
|
|
82
|
-
const { createData, addRoute, setGlobalData } = actions;
|
|
83
|
-
|
|
84
|
-
// Create data file that can be imported by components
|
|
85
|
-
const glossaryDataPath = await createData('glossary-data.json', JSON.stringify(content));
|
|
86
|
-
|
|
87
|
-
// Create a data file for the remark plugin to access glossary terms
|
|
88
|
-
const remarkGlossaryDataPath = await createData(
|
|
89
|
-
'remark-glossary-data.json',
|
|
90
|
-
JSON.stringify({
|
|
91
|
-
terms: content.terms || [],
|
|
92
|
-
routePath: routePath,
|
|
93
|
-
})
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
// Add glossary page route
|
|
97
|
-
addRoute({
|
|
98
|
-
path: routePath,
|
|
99
|
-
component: path.join(__dirname, 'components/GlossaryPage.js'),
|
|
100
|
-
exact: true,
|
|
101
|
-
modules: {
|
|
102
|
-
glossaryData: glossaryDataPath,
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// Expose global data for runtime lookups (used by GlossaryTerm)
|
|
107
|
-
setGlobalData({
|
|
108
|
-
terms: content.terms || [],
|
|
109
|
-
routePath,
|
|
110
|
-
});
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
getThemePath() {
|
|
114
|
-
return path.resolve(__dirname, './theme');
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
getPathsToWatch() {
|
|
118
|
-
return [path.resolve(context.siteDir, glossaryPath)];
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
async postBuild({ outDir }) {
|
|
122
|
-
// You can add any post-build steps here if needed
|
|
123
|
-
console.log('Glossary plugin: Build completed');
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Export remark plugin factory for use in markdown configuration
|
|
129
|
-
glossaryPlugin.remarkPlugin = require('./remark/glossary-terms');
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Helper function to get the configured remark plugin
|
|
133
|
-
* This can be used in docusaurus.config.js markdown configuration
|
|
134
|
-
*
|
|
135
|
-
* @param {object} pluginOptions - Plugin options from docusaurus.config.js
|
|
136
|
-
* @param {object} context - Docusaurus context
|
|
137
|
-
* @returns {function} Configured remark plugin
|
|
138
|
-
*/
|
|
139
|
-
glossaryPlugin.getRemarkPlugin = function (pluginOptions, context) {
|
|
140
|
-
const {
|
|
141
|
-
glossaryPath = 'glossary/glossary.json',
|
|
142
|
-
routePath = '/glossary',
|
|
143
|
-
siteDir = context.siteDir,
|
|
144
|
-
} = pluginOptions;
|
|
145
|
-
|
|
146
|
-
return [
|
|
147
|
-
require('./remark/glossary-terms'),
|
|
148
|
-
{
|
|
149
|
-
glossaryPath,
|
|
150
|
-
routePath,
|
|
151
|
-
siteDir,
|
|
152
|
-
},
|
|
153
|
-
];
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
module.exports = glossaryPlugin;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|