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 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. No manual MDX pipeline wiring is required when `autoLinkTerms` is enabled (default).
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
- **That’s it!** On Docusaurus v3, the remark plugin is automatically configured via the plugin’s `configureMarkdown` hook no manual `markdown.remarkPlugins` setup needed.
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
- **Automatic Configuration:** The remark plugin is automatically configured when `autoLinkTerms` is `true` (the default). You don't need to manually configure `markdown.remarkPlugins`!
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
- **Advanced: Manual Remark Plugin Configuration**
210
+ **Alternative: Using remarkPlugin directly**
147
211
 
148
- If you need more control or want to disable automatic detection, you can manually configure the remark plugin:
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 youre on `@docusaurus/core@^3` and `react@^18`.
191
- - Ensure the plugin is listed in `plugins` and `autoLinkTerms` is not disabled.
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**: set `autoLinkTerms: false` and add the remark plugin manually (see above), or only use the `<GlossaryTerm />` component where you want explicit control.
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 | Type | Default | Description |
276
- | --------------- | ------- | -------------------------- | ---------------------------------------------------------------------------------- |
277
- | `glossaryPath` | string | `'glossary/glossary.json'` | Path to glossary JSON file relative to site directory |
278
- | `routePath` | string | `'/glossary'` | URL path for glossary page |
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 `autoLinkTerms` is `true` (the default) in your plugin configuration
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
- // Support both CJS and ESM exports of unist-util-visit
2
- let visit = require('unist-util-visit');
3
- visit = visit && visit.visit ? visit.visit : visit;
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
- ? textLower[afterIndex]
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 (!isWordBoundary && afterChar === 'e' && afterIndex + 1 < textLower.length && textLower[afterIndex + 1] === 's') {
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 (empty value + estree)
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: { type: 'Literal', value: '@theme/GlossaryTerm' },
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-primary);
7
+ color: var(--ifm-font-color-base);
8
8
  text-decoration: none;
9
- border-bottom: 1px dotted var(--ifm-color-primary);
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-bottom-color: var(--ifm-background-surface-color);
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-bottom-color: var(--ifm-color-emphasis-300);
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-top-color: var(--ifm-background-surface-color);
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-top-color: var(--ifm-color-emphasis-300);
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-bottom-color: var(--ifm-background-surface-color);
116
+ border-top-color: var(--ifm-background-surface-color);
115
117
  }
116
118
  [data-theme='dark'] .tooltipTop::before {
117
- border-bottom-color: var(--ifm-color-emphasis-400);
119
+ border-top-color: var(--ifm-color-emphasis-400);
118
120
  }
119
121
  [data-theme='dark'] .tooltipBottom::after {
120
- border-top-color: var(--ifm-background-surface-color);
122
+ border-bottom-color: var(--ifm-background-surface-color);
121
123
  }
122
124
  [data-theme='dark'] .tooltipBottom::before {
123
- border-top-color: var(--ifm-color-emphasis-400);
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.0",
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
- "main": "index.js",
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
- "index.js",
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
- "prepublishOnly": "npm test",
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