docusaurus-plugin-terminology 0.1.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/LICENSE +21 -0
- package/README.md +213 -0
- package/index.js +181 -0
- package/package.json +45 -0
- package/remark/term-link-transformer.js +64 -0
- package/src/index.d.ts +11 -0
- package/theme/Glossary.jsx +74 -0
- package/theme/tooltip.js +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Jürgen Schilke.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# docusaurus-plugin-terminology
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Overview
|
|
5
|
+
|
|
6
|
+
Terminology/glossary plugin for [Docusaurus](https://github.com/facebook/docusaurus) 3 with:
|
|
7
|
+
|
|
8
|
+
- glossary generation
|
|
9
|
+
- term tooltips
|
|
10
|
+
- MDX support (Built on MDX AST transformations instead of raw HTML replacement for improved Docusaurus 3 compatibility)
|
|
11
|
+
- i18n support
|
|
12
|
+
- Tippy.js integration
|
|
13
|
+
|
|
14
|
+
This plugin is inspired by [@grnet/docusaurus-terminology](https://github.com/grnet/docusaurus-terminology). It's based on the same general concept but uses MDX remark transformations instead of webpack loader injection and uses a more robust handling of the terms' links.
|
|
15
|
+
Compatible with Docusaurus 3.x and React 18.
|
|
16
|
+
Tested with Docusaurus 3.7.0.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Define terms as Markdown files.
|
|
22
|
+
- Glossary component: Automatic glossary data aggregation from Markdown term files in `docs/glossary`.
|
|
23
|
+
- Tooltips on glossary links: Automatically transforms glossary links into interactive tooltip components.
|
|
24
|
+
- Locale-aware glossary support: Supports multiple locales and ensures fallback to the default locale for missing terms.
|
|
25
|
+
- Compatible with Docusaurus 3 + React 18
|
|
26
|
+
- SSR-safe MDX integration
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Compatibility
|
|
30
|
+
|
|
31
|
+
| Package | Version |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Docusaurus | 3.7.0+ |
|
|
34
|
+
| React | 18+ |
|
|
35
|
+
| Node.js | 18+ recommended |
|
|
36
|
+
|
|
37
|
+
The plugin supports `.md` and `.mdx` files.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Install the plugin using npm or yarn:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install docusaurus-plugin-terminology
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
or
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
yarn add docusaurus-plugin-terminology
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
Add the plugin to `docusaurus.config.js`:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
plugins: [
|
|
61
|
+
[
|
|
62
|
+
'docusaurus-plugin-terminology',
|
|
63
|
+
{
|
|
64
|
+
// options
|
|
65
|
+
termsDir: 'terms', // default: 'terms'
|
|
66
|
+
routeBasePath: '/docs/terms/', // default: '/docs/terms/'
|
|
67
|
+
hideTermsFromSidebar: true, // when true, plugin will create _category_.yml to hide individual term pages
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
],
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
### Defining terms
|
|
78
|
+
|
|
79
|
+
Create a `terms` directory within `docs` (the plugin default).
|
|
80
|
+
|
|
81
|
+
Add Markdown files for your terms in that directory.
|
|
82
|
+
Structure example:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
docs/
|
|
86
|
+
├── terms/ # terms definitions
|
|
87
|
+
│ ├── ai.md
|
|
88
|
+
│ ├── css.md
|
|
89
|
+
│ ├── ftp.md
|
|
90
|
+
│ ├── html.md
|
|
91
|
+
│ └── ...
|
|
92
|
+
└── terms.md # glossary page (id: glossary)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
#### Example Term File
|
|
97
|
+
|
|
98
|
+
Each file should contain metadata in the frontmatter:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
---
|
|
102
|
+
title: AI - »Artificial Intelligence«
|
|
103
|
+
description: Intelligence of machines.
|
|
104
|
+
---
|
|
105
|
+
```
|
|
106
|
+
```markdown
|
|
107
|
+
The capability of computational systems to perform tasks typically associated with human intelligence, such as learning, reasoning, problem-solving, perception, and decision-making. It is a field of research in engineering, mathematics and computer science that develops and studies methods and software that enable machines to perceive their environment and use learning and intelligence to take actions that maximize their chances of achieving defined goals.
|
|
108
|
+
|
|
109
|
+
*From [Artificial intelligence - Wikipedia](https://en.wikipedia.org/wiki/Artificial_intelligence#Techniques)*
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Linking terms
|
|
114
|
+
|
|
115
|
+
Link to terms in your Markdown files using the `/docs/terms/TERM_SLUG` format (or adjust to your `termsDir`).
|
|
116
|
+
Example:
|
|
117
|
+
|
|
118
|
+
```markdown
|
|
119
|
+
[AI](/docs/terms/ai)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The plugin will automatically transform these links into tooltips.
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### Glossary Page
|
|
126
|
+
|
|
127
|
+
The plugin provides a `Glossary` component that displays all glossary terms grouped alphabetically. You can use this component in your custom pages.
|
|
128
|
+
|
|
129
|
+
#### Create a glossary page
|
|
130
|
+
|
|
131
|
+
E.g. `glossary.md` (the file name doesn't matter)
|
|
132
|
+
|
|
133
|
+
```markdown
|
|
134
|
+
---
|
|
135
|
+
id: glossary
|
|
136
|
+
title: Glossary
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
import Glossary from '@theme/Glossary';
|
|
140
|
+
|
|
141
|
+
<Glossary />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### i18n
|
|
145
|
+
|
|
146
|
+
Localized term definitions can be placed in:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
i18n/de/docusaurus-plugin-content-docs/current/terms/
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
## How it works
|
|
154
|
+
|
|
155
|
+
1. The plugin scans `docs/terms` (or the directory configured via `termsDir`)
|
|
156
|
+
2. Term metadata is extracted from frontmatter
|
|
157
|
+
3. Markdown links matching `/docs/terms/*` (or matching your `routeBasePath`) are transformed into MDX tooltip components
|
|
158
|
+
4. Generates a static glossary JSON data source for tooltip rendering
|
|
159
|
+
5. Glossary data is exposed globally to Docusaurus theme components
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### Internal Architecture
|
|
163
|
+
Markdown links
|
|
164
|
+
↓
|
|
165
|
+
remark plugin transformation
|
|
166
|
+
↓
|
|
167
|
+
<TermTooltip />
|
|
168
|
+
↓
|
|
169
|
+
MDX component mapping
|
|
170
|
+
↓
|
|
171
|
+
Tippy.js tooltip rendering
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
### Styling
|
|
175
|
+
|
|
176
|
+
Override tooltip styles via CSS.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## File Structure
|
|
180
|
+
|
|
181
|
+
The plugin's file structure is as follows:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
docusaurus-plugin-terminology
|
|
185
|
+
├── remark
|
|
186
|
+
│ └── term-link-transformer.js
|
|
187
|
+
├── theme
|
|
188
|
+
│ ├── Glossary.jsx
|
|
189
|
+
│ └── tooltip.js
|
|
190
|
+
├── index.js
|
|
191
|
+
├── LICENSE
|
|
192
|
+
└── README.md
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- `remark/term-link-transformer.js`: Transforms glossary term links in Markdown files into `TermTooltip` components.
|
|
196
|
+
- `theme/Glossary.jsx`: Renders the glossary page.
|
|
197
|
+
- `theme/tooltip.js`: Handles tooltips for glossary terms.
|
|
198
|
+
- `index.js`: Main plugin entry point.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
Contributions are welcome! Feel free to open issues or submit pull requests to improve the plugin.
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
## Acknowledgments
|
|
212
|
+
|
|
213
|
+
This plugin is inspired by [@grnet/docusaurus-terminology](https://github.com/grnet/docusaurus-terminology) and aims to provide a more robust and modern implementation.
|
package/index.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// docusaurus-plugin-terminology/index.js
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const matter = require('gray-matter');
|
|
5
|
+
|
|
6
|
+
module.exports = function (context, options = {}) {
|
|
7
|
+
|
|
8
|
+
// Provide sensible defaults and read hide flag
|
|
9
|
+
options = Object.assign(
|
|
10
|
+
{
|
|
11
|
+
termsDir: 'terms',
|
|
12
|
+
routeBasePath: '/docs/terms/',
|
|
13
|
+
hideTermsFromSidebar: false,
|
|
14
|
+
},
|
|
15
|
+
options || {}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const termsDir = options.termsDir || 'terms';
|
|
19
|
+
const routeBasePath = options.routeBasePath || '/docs/terms/';
|
|
20
|
+
const hideTermsFromSidebar = options.hideTermsFromSidebar === true;
|
|
21
|
+
|
|
22
|
+
//console.log('[plugin] docusaurus-plugin-terminology loaded');
|
|
23
|
+
return {
|
|
24
|
+
name: 'docusaurus-plugin-terminology',
|
|
25
|
+
|
|
26
|
+
extendMarkdownOptions(mdOptions) {
|
|
27
|
+
mdOptions.remarkPlugins = mdOptions.remarkPlugins || [];
|
|
28
|
+
mdOptions.remarkPlugins.push([
|
|
29
|
+
require('./remark/term-link-transformer'),
|
|
30
|
+
{
|
|
31
|
+
termsDir,
|
|
32
|
+
routeBasePath,
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
//console.log('[plugin] extendMarkdownOptions: registered termLinkTransformer');
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Optionally write a _category_.yml into the terms docs directory (and per-locale equivalents)
|
|
40
|
+
* so that the terms directory is represented by a single category entry that can be hidden
|
|
41
|
+
* (className: hidden). This prevents the individual generated term files from showing up
|
|
42
|
+
* in the main docs sidebar.
|
|
43
|
+
*/
|
|
44
|
+
async loadContent() {
|
|
45
|
+
if (!hideTermsFromSidebar) return null;
|
|
46
|
+
|
|
47
|
+
const CATEGORY_FILENAME = '_category_.yml';
|
|
48
|
+
const makeCategoryYaml = (label = 'Glossar') => `label: ${label}
|
|
49
|
+
className: hidden
|
|
50
|
+
collapsed: true
|
|
51
|
+
link:
|
|
52
|
+
type: doc
|
|
53
|
+
id: glossary
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const writeIfMissing = (docsDir) => {
|
|
57
|
+
try {
|
|
58
|
+
if (!fs.existsSync(docsDir)) return;
|
|
59
|
+
const catPath = path.join(docsDir, CATEGORY_FILENAME);
|
|
60
|
+
if (fs.existsSync(catPath)) {
|
|
61
|
+
// Respect an existing file; do not overwrite
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
fs.writeFileSync(catPath, makeCategoryYaml(), { encoding: 'utf8' });
|
|
65
|
+
console.info(`[docusaurus-plugin-terminology] wrote ${CATEGORY_FILENAME} to ${docsDir}`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
// Non-fatal: warn and continue
|
|
68
|
+
console.warn(`[docusaurus-plugin-terminology] failed to write ${CATEGORY_FILENAME} to ${docsDir}: ${err.message}`);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// default locale docs dir
|
|
73
|
+
const defaultDocsDir = path.resolve(context.siteDir, `docs/${termsDir}`);
|
|
74
|
+
writeIfMissing(defaultDocsDir);
|
|
75
|
+
|
|
76
|
+
// localized docs dirs (if i18n present)
|
|
77
|
+
const { i18n } = context || {};
|
|
78
|
+
if (i18n && Array.isArray(i18n.locales)) {
|
|
79
|
+
for (const locale of i18n.locales) {
|
|
80
|
+
if (locale === i18n.defaultLocale) continue;
|
|
81
|
+
const localizedDocsDir = path.resolve(
|
|
82
|
+
context.siteDir,
|
|
83
|
+
`i18n/${locale}/docusaurus-plugin-content-docs/current/${termsDir}`
|
|
84
|
+
);
|
|
85
|
+
writeIfMissing(localizedDocsDir);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return null;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Called when content has been loaded.
|
|
94
|
+
* Processes glossary terms data for different locales and generates a glossary JSON file.
|
|
95
|
+
* @param {Object} context - The context object containing siteDir, i18n, and actions.
|
|
96
|
+
*/
|
|
97
|
+
async contentLoaded({ actions }) {
|
|
98
|
+
const { siteDir, i18n } = context;
|
|
99
|
+
const { defaultLocale, locales } = i18n;
|
|
100
|
+
const { setGlobalData, createData } = actions;
|
|
101
|
+
|
|
102
|
+
const termsData = {};
|
|
103
|
+
|
|
104
|
+
for (const locale of locales) {
|
|
105
|
+
const docsDir = locale === defaultLocale
|
|
106
|
+
? path.resolve(siteDir, `docs/${termsDir}`)
|
|
107
|
+
: path.resolve(
|
|
108
|
+
siteDir,
|
|
109
|
+
`i18n/${locale}/docusaurus-plugin-content-docs/current/${termsDir}`
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
if (!fs.existsSync(docsDir)) continue;
|
|
113
|
+
|
|
114
|
+
const termFiles = fs.readdirSync(docsDir).filter((f) => (f.endsWith('.md') || f.endsWith('.mdx')) && f !== 'index.md');
|
|
115
|
+
|
|
116
|
+
const termsArray = [];
|
|
117
|
+
|
|
118
|
+
for (const filename of termFiles) {
|
|
119
|
+
const slug = filename.replace(/\.mdx?$/, '');
|
|
120
|
+
const filepath = path.join(docsDir, filename);
|
|
121
|
+
const fileContent = fs.readFileSync(filepath, 'utf-8');
|
|
122
|
+
const { data, content } = matter(fileContent);
|
|
123
|
+
|
|
124
|
+
termsArray.push({
|
|
125
|
+
slug,
|
|
126
|
+
name: data.title || slug,
|
|
127
|
+
description: data.description || content.trim().split('\n')[0],
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Sort termsArray alphabetically by name
|
|
132
|
+
termsArray.sort((a, b) => a.name.localeCompare(b.name));
|
|
133
|
+
|
|
134
|
+
// Convert sorted array back to object
|
|
135
|
+
termsData[locale] = termsArray.reduce((acc, term) => {
|
|
136
|
+
acc[term.slug] = {
|
|
137
|
+
name: term.name,
|
|
138
|
+
description: term.description,
|
|
139
|
+
};
|
|
140
|
+
return acc;
|
|
141
|
+
}, {});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Fallback: fill in missing terms for other locales using default locale
|
|
145
|
+
for (const locale of Object.keys(termsData)) {
|
|
146
|
+
if (locale === defaultLocale) continue;
|
|
147
|
+
for (const [slug, entry] of Object.entries(termsData[defaultLocale] || {})) {
|
|
148
|
+
if (!termsData[locale]?.[slug]) {
|
|
149
|
+
if (!termsData[locale]) termsData[locale] = {};
|
|
150
|
+
termsData[locale][slug] = entry;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
//console.log('[plugin] termsData:', termsData);
|
|
156
|
+
//console.log('Current locale:', i18n.currentLocale);
|
|
157
|
+
|
|
158
|
+
// Write static glossary JSON file (default locale only)
|
|
159
|
+
const outputJson = JSON.stringify(termsData[defaultLocale] || {}, null, 2);
|
|
160
|
+
const glossaryDataPath = await createData('glossary.json', outputJson);
|
|
161
|
+
//console.log('[plugin] glossary.json generated at:', glossaryDataPath);
|
|
162
|
+
//console.log('[plugin] setGlobalData terms:', termsData);
|
|
163
|
+
|
|
164
|
+
// Expose for use in components
|
|
165
|
+
setGlobalData({
|
|
166
|
+
terms: termsData,
|
|
167
|
+
glossaryDataPath,
|
|
168
|
+
routeBasePath,
|
|
169
|
+
termsDir,
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
getThemePath() {
|
|
174
|
+
return path.resolve(__dirname, './theme');
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
getClientModules() {
|
|
178
|
+
return [path.resolve(__dirname, './theme/tooltip.js')];
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "docusaurus-plugin-terminology",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Terminology and glossary plugin for Docusaurus 3",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"docusaurus",
|
|
9
|
+
"docusaurus-plugin",
|
|
10
|
+
"glossary",
|
|
11
|
+
"terminology",
|
|
12
|
+
"tooltip",
|
|
13
|
+
"mdx"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/schilke/docusaurus-plugin-terminology.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/schilke/docusaurus-plugin-terminology/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/schilke/docusaurus-plugin-terminology#readme",
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@docusaurus/core": "^3.0.0",
|
|
25
|
+
"@docusaurus/preset-classic": "^3.0.0",
|
|
26
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
27
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@tippyjs/react": "^4.2.6",
|
|
31
|
+
"gray-matter": "^4.0.3",
|
|
32
|
+
"unist-util-visit": "^5.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"jest": "^29.0.0",
|
|
36
|
+
"memfs": "^3.4.13"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "jest --color"
|
|
40
|
+
}
|
|
41
|
+
,
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transforms links to terms in Docusaurus MDX files
|
|
3
|
+
* to use a custom TermTooltip component.
|
|
4
|
+
* /docusaurus-plugin-terminology/remark/term-link-transformer.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { visit } = require('unist-util-visit');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Transforms links to glossary terms in the tree to MDX JSX elements.
|
|
11
|
+
* @returns {Function} A function that transforms links in the tree to MDX JSX elements.
|
|
12
|
+
*/
|
|
13
|
+
function termLinkTransformer(options = {}) {
|
|
14
|
+
|
|
15
|
+
const routeBasePath =
|
|
16
|
+
options.routeBasePath || '/docs/terms/';
|
|
17
|
+
return (tree, file) => {
|
|
18
|
+
visit(tree, 'link', (node, index, parent) => {
|
|
19
|
+
const href = node.url || '';
|
|
20
|
+
|
|
21
|
+
// Match links like [TERM](/docs/glossary/TERM)
|
|
22
|
+
const escapedRoute = routeBasePath.replace(
|
|
23
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
24
|
+
'\\$&'
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const termRegex = new RegExp(
|
|
28
|
+
`^${escapedRoute}[\\w-]+$`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (termRegex.test(href)) {
|
|
32
|
+
const termSlug = href.replace(routeBasePath, '');
|
|
33
|
+
const termName = node.children.map((n) => n.value || '').join('');
|
|
34
|
+
|
|
35
|
+
// Replace with a valid MDX JSX element instead of raw HTML
|
|
36
|
+
const jsxNode = {
|
|
37
|
+
type: 'mdxJsxTextElement',
|
|
38
|
+
name: 'TermTooltip',
|
|
39
|
+
attributes: [
|
|
40
|
+
{
|
|
41
|
+
type: 'mdxJsxAttribute',
|
|
42
|
+
name: 'term',
|
|
43
|
+
value: termSlug,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: 'mdxJsxAttribute',
|
|
47
|
+
name: 'href',
|
|
48
|
+
value: href,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
children: [
|
|
52
|
+
{
|
|
53
|
+
type: 'text',
|
|
54
|
+
value: termName,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
parent.children[index] = jsxNode;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
module.exports = termLinkTransformer;
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Plugin } from '@docusaurus/types';
|
|
2
|
+
|
|
3
|
+
export interface TerminologyPluginOptions {
|
|
4
|
+
termsDir?: string;
|
|
5
|
+
routeBasePath?: string;
|
|
6
|
+
hideTermsFromSidebar?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare function pluginTerminology(context: any, options?: TerminologyPluginOptions): Plugin;
|
|
10
|
+
|
|
11
|
+
export default pluginTerminology;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// docusaurus-plugin-terminology/theme/Glossary.jsx
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { usePluginData } from '@docusaurus/useGlobalData';
|
|
4
|
+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
5
|
+
|
|
6
|
+
export default function Glossary() {
|
|
7
|
+
const { i18n } = useDocusaurusContext();
|
|
8
|
+
const pluginData = usePluginData('docusaurus-plugin-terminology');
|
|
9
|
+
|
|
10
|
+
const localeTerms =
|
|
11
|
+
pluginData.terms?.[i18n.currentLocale] || {};
|
|
12
|
+
|
|
13
|
+
const routeBasePath =
|
|
14
|
+
pluginData.routeBasePath || '/docs/terms/';
|
|
15
|
+
const termEntries = Object.entries(localeTerms);
|
|
16
|
+
|
|
17
|
+
if (termEntries.length === 0) {
|
|
18
|
+
return <p>No glossary terms found.</p>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Group terms by their starting letter
|
|
22
|
+
const groupedTerms = termEntries.reduce((acc, [key, { name, description }]) => {
|
|
23
|
+
const firstLetter = name[0].toUpperCase();
|
|
24
|
+
if (!acc[firstLetter]) {
|
|
25
|
+
acc[firstLetter] = [];
|
|
26
|
+
}
|
|
27
|
+
acc[firstLetter].push({ key, name, description });
|
|
28
|
+
return acc;
|
|
29
|
+
}, {});
|
|
30
|
+
|
|
31
|
+
const alphabet = Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i));
|
|
32
|
+
|
|
33
|
+
// Filter out letters with no terms
|
|
34
|
+
const filteredAlphabet = alphabet.filter((letter) => groupedTerms[letter]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className="container">
|
|
38
|
+
{/* A-Z Index */}
|
|
39
|
+
<div className="container margin-bottom--lg">
|
|
40
|
+
{filteredAlphabet.map((letter) => (
|
|
41
|
+
<a
|
|
42
|
+
className="button button--sm button--outline button--primary col col--1 margin--xs"
|
|
43
|
+
key={letter}
|
|
44
|
+
href={`#${letter}`}
|
|
45
|
+
>
|
|
46
|
+
{letter}
|
|
47
|
+
</a>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
{/* Terms List */}
|
|
52
|
+
<div>
|
|
53
|
+
{filteredAlphabet.map((letter) => (
|
|
54
|
+
<div key={letter} id={letter} className="container margin-vert--xs padding-vert--lg shadow--md">
|
|
55
|
+
<h2>{letter}</h2>
|
|
56
|
+
<ul>
|
|
57
|
+
{groupedTerms[letter].map(({ key, name, description }) => (
|
|
58
|
+
<li
|
|
59
|
+
key={key}
|
|
60
|
+
style={{ listStyleType: 'none' }}
|
|
61
|
+
>
|
|
62
|
+
<strong>
|
|
63
|
+
<a href={`${routeBasePath}${key}`}>{name}</a>:
|
|
64
|
+
</strong>{' '}
|
|
65
|
+
{description}
|
|
66
|
+
</li>
|
|
67
|
+
))}
|
|
68
|
+
</ul>
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
package/theme/tooltip.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// docusaurus-plugin-terminology/theme/tooltip.js
|
|
2
|
+
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
3
|
+
import tippy from 'tippy.js';
|
|
4
|
+
import 'tippy.js/dist/tippy.css';
|
|
5
|
+
//import 'tippy.js/themes/light-border.css';
|
|
6
|
+
import 'tippy.js/themes/material.css';
|
|
7
|
+
|
|
8
|
+
if (ExecutionEnvironment.canUseDOM) {
|
|
9
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
10
|
+
const locale = document.documentElement.lang || 'en';
|
|
11
|
+
const allTerms = window.Docusaurus?.__pluginGlobalData?.['docusaurus-plugin-terminology'] || {};
|
|
12
|
+
const terms = allTerms[locale] || {};
|
|
13
|
+
|
|
14
|
+
document.querySelectorAll('.term-tooltip').forEach((el) => {
|
|
15
|
+
const slug = el.dataset.term;
|
|
16
|
+
const info = terms[slug];
|
|
17
|
+
if (!info) return;
|
|
18
|
+
|
|
19
|
+
tippy(el, {
|
|
20
|
+
content: info.description,
|
|
21
|
+
allowHTML: true,
|
|
22
|
+
//theme: 'light-border',
|
|
23
|
+
theme: 'material',
|
|
24
|
+
placement: 'top-end',
|
|
25
|
+
delay: [100, 50],
|
|
26
|
+
maxWidth: 300,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|