docusaurus-plugin-glossary 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/chunk-22LFZL7L.js +109 -0
- package/dist/chunk-22LFZL7L.js.map +1 -0
- package/dist/chunk-7Z37JEHW.js +257 -0
- package/dist/chunk-7Z37JEHW.js.map +1 -0
- package/dist/chunk-WYKSBP3X.js +331 -0
- package/dist/chunk-WYKSBP3X.js.map +1 -0
- package/dist/client/index.cjs +55 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.js +10 -21
- package/dist/client/index.js.map +1 -0
- package/dist/components/GlossaryPage.cjs +131 -0
- package/dist/components/GlossaryPage.cjs.map +1 -0
- package/dist/components/GlossaryPage.js +75 -113
- package/dist/components/GlossaryPage.js.map +1 -0
- package/dist/index.cjs +724 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +176 -0
- package/dist/index.d.ts +85 -11
- package/dist/index.js +23 -173
- package/dist/index.js.map +1 -0
- package/dist/preset.cjs +775 -0
- package/dist/preset.cjs.map +1 -0
- package/dist/preset.d.cts +98 -0
- package/dist/preset.d.ts +8 -7
- package/dist/preset.js +79 -143
- package/dist/preset.js.map +1 -0
- package/dist/remark/glossary-terms.cjs +365 -0
- package/dist/remark/glossary-terms.cjs.map +1 -0
- package/dist/remark/glossary-terms.js +9 -440
- package/dist/remark/glossary-terms.js.map +1 -0
- package/dist/{theme/GlossaryTerm/styles.module.css → styles.module-QQW7ISLV.module.css} +2 -4
- package/dist/theme/GlossaryTerm/index.cjs +138 -0
- package/dist/theme/GlossaryTerm/index.cjs.map +1 -0
- package/dist/theme/GlossaryTerm/index.js +56 -90
- package/dist/theme/GlossaryTerm/index.js.map +1 -0
- package/dist/validation.cjs +283 -0
- package/dist/validation.cjs.map +1 -0
- package/dist/validation.d.cts +2 -0
- package/dist/validation.d.ts +2 -44
- package/dist/validation.js +11 -256
- package/dist/validation.js.map +1 -0
- package/package.json +27 -32
- package/dist/components/GlossaryPage.test.js +0 -205
- package/dist/index.d.ts.map +0 -1
- package/dist/preset.d.ts.map +0 -1
- package/dist/remark/glossary-terms.d.ts +0 -28
- package/dist/remark/glossary-terms.d.ts.map +0 -1
- package/dist/theme/GlossaryTerm/index.test.js +0 -143
- package/dist/validation.d.ts.map +0 -1
- /package/dist/{components/GlossaryPage.module.css → GlossaryPage.module-M4DEUP4X.module.css} +0 -0
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ A comprehensive Docusaurus plugin that provides glossary functionality with an a
|
|
|
63
63
|
|
|
64
64
|
```json
|
|
65
65
|
{
|
|
66
|
+
"title": "Glossary",
|
|
66
67
|
"description": "A collection of technical terms and their definitions",
|
|
67
68
|
"terms": [
|
|
68
69
|
{
|
|
@@ -108,6 +109,7 @@ Create a JSON file at `glossary/glossary.json` (or your configured path) in your
|
|
|
108
109
|
|
|
109
110
|
```json
|
|
110
111
|
{
|
|
112
|
+
"title": "Glossary",
|
|
111
113
|
"description": "A collection of technical terms and their definitions",
|
|
112
114
|
"terms": [
|
|
113
115
|
{
|
|
@@ -136,6 +138,8 @@ Create a JSON file at `glossary/glossary.json` (or your configured path) in your
|
|
|
136
138
|
- `abbreviation` (string): The full form if the term is an abbreviation
|
|
137
139
|
- `relatedTerms` (string[]): Array of related term names that link to other glossary entries
|
|
138
140
|
- `id` (string): Custom ID for linking (auto-generated from term name if not provided)
|
|
141
|
+
- `autoLink` (boolean): Set to `false` to opt a term out of automatic linking (default: `true`)
|
|
142
|
+
- `aliases` (string[]): Additional phrases that should also auto-link to this term. Useful for inflections (e.g. `["cleaning", "cleaned"]` for `clean`) or alternate forms. The rendered link and tooltip always use the canonical `term`
|
|
139
143
|
|
|
140
144
|
### Step 2: Configure the Plugin
|
|
141
145
|
|
|
@@ -455,6 +459,7 @@ See the [Usage Guide](#usage-guide) section above for complete examples. Here ar
|
|
|
455
459
|
|
|
456
460
|
```json
|
|
457
461
|
{
|
|
462
|
+
"title": "Glossary",
|
|
458
463
|
"description": "Technical terms used in our documentation",
|
|
459
464
|
"terms": [
|
|
460
465
|
{
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GlossaryValidationError,
|
|
3
|
+
validateGlossaryData
|
|
4
|
+
} from "./chunk-7Z37JEHW.js";
|
|
5
|
+
import {
|
|
6
|
+
remarkGlossaryTerms
|
|
7
|
+
} from "./chunk-WYKSBP3X.js";
|
|
8
|
+
|
|
9
|
+
// src/index.ts
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
import fs from "fs-extra";
|
|
13
|
+
import validatePeerDependencies from "validate-peer-dependencies";
|
|
14
|
+
var currentFilePath = fileURLToPath(import.meta.url);
|
|
15
|
+
var currentDir = path.dirname(currentFilePath);
|
|
16
|
+
validatePeerDependencies(currentDir);
|
|
17
|
+
function glossaryPlugin(context, options = {}) {
|
|
18
|
+
const { glossaryPath = "glossary/glossary.json", routePath = "/glossary" } = options;
|
|
19
|
+
return {
|
|
20
|
+
name: "docusaurus-plugin-glossary",
|
|
21
|
+
getClientModules() {
|
|
22
|
+
return [path.resolve(currentDir, "./client/index.js")];
|
|
23
|
+
},
|
|
24
|
+
async loadContent() {
|
|
25
|
+
const glossaryFilePath = path.resolve(context.siteDir, glossaryPath);
|
|
26
|
+
if (await fs.pathExists(glossaryFilePath)) {
|
|
27
|
+
try {
|
|
28
|
+
const rawData = await fs.readJson(glossaryFilePath);
|
|
29
|
+
const validationResult = validateGlossaryData(rawData, { throwOnError: false });
|
|
30
|
+
if (!validationResult.valid) {
|
|
31
|
+
console.warn(
|
|
32
|
+
`[glossary-plugin] Glossary file has validation errors at ${glossaryFilePath}:`
|
|
33
|
+
);
|
|
34
|
+
validationResult.errors.forEach((err) => {
|
|
35
|
+
console.warn(` - [${err.field}] ${err.message}`);
|
|
36
|
+
});
|
|
37
|
+
console.warn("[glossary-plugin] Proceeding with valid terms only.");
|
|
38
|
+
}
|
|
39
|
+
return validationResult.data;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof GlossaryValidationError) {
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Failed to parse glossary file at ${glossaryFilePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
console.warn(`Glossary file not found at ${glossaryFilePath}. Using empty glossary.`);
|
|
50
|
+
return { terms: [] };
|
|
51
|
+
},
|
|
52
|
+
async contentLoaded({ content, actions }) {
|
|
53
|
+
const { createData, addRoute, setGlobalData } = actions;
|
|
54
|
+
const glossaryContent = content;
|
|
55
|
+
const glossaryDataPath = await createData(
|
|
56
|
+
"glossary-data.json",
|
|
57
|
+
JSON.stringify(glossaryContent)
|
|
58
|
+
);
|
|
59
|
+
await createData(
|
|
60
|
+
"remark-glossary-data.json",
|
|
61
|
+
JSON.stringify({
|
|
62
|
+
terms: glossaryContent.terms || [],
|
|
63
|
+
routePath
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
addRoute({
|
|
67
|
+
path: routePath,
|
|
68
|
+
component: path.join(currentDir, "components/GlossaryPage.js"),
|
|
69
|
+
exact: true,
|
|
70
|
+
modules: {
|
|
71
|
+
glossaryData: glossaryDataPath
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
setGlobalData({
|
|
75
|
+
terms: glossaryContent.terms || [],
|
|
76
|
+
routePath
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
getThemePath() {
|
|
80
|
+
return path.resolve(currentDir, "./theme");
|
|
81
|
+
},
|
|
82
|
+
getPathsToWatch() {
|
|
83
|
+
return [path.resolve(context.siteDir, glossaryPath)];
|
|
84
|
+
},
|
|
85
|
+
async postBuild() {
|
|
86
|
+
console.log("Glossary plugin: Build completed");
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
var remarkPlugin = remarkGlossaryTerms;
|
|
91
|
+
function getRemarkPlugin(pluginOptions, context) {
|
|
92
|
+
const { glossaryPath = "glossary/glossary.json", routePath = "/glossary" } = pluginOptions;
|
|
93
|
+
const siteDir = context?.siteDir;
|
|
94
|
+
return [
|
|
95
|
+
remarkGlossaryTerms,
|
|
96
|
+
{
|
|
97
|
+
glossaryPath,
|
|
98
|
+
routePath,
|
|
99
|
+
siteDir
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
glossaryPlugin,
|
|
106
|
+
remarkPlugin,
|
|
107
|
+
getRemarkPlugin
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=chunk-22LFZL7L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @ts-ignore\nimport path from 'path';\n// @ts-ignore\nimport { fileURLToPath } from 'url';\nimport fs from 'fs-extra';\nimport type { LoadContext, Plugin } from '@docusaurus/types';\nimport validatePeerDependencies from 'validate-peer-dependencies';\nimport remarkGlossaryTerms from './remark/glossary-terms.js';\nimport { validateGlossaryData, GlossaryValidationError } from './validation.js';\n\n// Standard ES module directory resolution\nconst currentFilePath = fileURLToPath(import.meta.url);\nconst currentDir = path.dirname(currentFilePath);\n\n// Validate peer dependencies at module load time\nvalidatePeerDependencies(currentDir);\n\nexport interface GlossaryPluginOptions {\n glossaryPath?: string;\n routePath?: string;\n autoLinkTerms?: boolean;\n}\n\nexport interface GlossaryTerm {\n term: string;\n definition: string;\n abbreviation?: string;\n relatedTerms?: string[];\n id?: string;\n autoLink?: boolean;\n aliases?: string[];\n}\n\nexport interface GlossaryData {\n title?: string;\n description?: string;\n terms: GlossaryTerm[];\n}\n\n/**\n * Docusaurus Glossary Plugin\n *\n * A plugin that provides glossary functionality with:\n * - Glossary terms defined in a JSON file\n * - Auto-generated glossary page with term definitions\n * - GlossaryTerm component for inline definitions with interactive tooltips\n * - Automatic client-side initialization via getClientModules() (no manual imports needed)\n * - Optional automatic glossary term detection in markdown files via remark plugin\n *\n * ## Basic Usage (Manual Term Markup)\n *\n * Just install the plugin - the GlossaryTerm component is automatically available:\n * ```javascript\n * module.exports = {\n * plugins: [\n * ['docusaurus-plugin-glossary', {\n * glossaryPath: 'glossary/glossary.json',\n * routePath: '/glossary',\n * }],\n * ],\n * };\n * ```\n *\n * Then use `<GlossaryTerm>` in your MDX files without importing:\n * ```mdx\n * <GlossaryTerm term=\"API\">API</GlossaryTerm>\n * ```\n *\n * ## Advanced Usage (Automatic Term Detection)\n *\n * To automatically detect and link glossary terms in markdown, add the remark plugin:\n * ```javascript\n * const glossaryPlugin = require('docusaurus-plugin-glossary');\n *\n * module.exports = {\n * presets: [\n * ['@docusaurus/preset-classic', {\n * docs: {\n * remarkPlugins: [\n * glossaryPlugin.getRemarkPlugin({\n * glossaryPath: 'glossary/glossary.json',\n * routePath: '/glossary',\n * }, { siteDir: __dirname }),\n * ],\n * },\n * }],\n * ],\n * plugins: [\n * ['docusaurus-plugin-glossary', {\n * glossaryPath: 'glossary/glossary.json',\n * routePath: '/glossary',\n * }],\n * ],\n * };\n * ```\n *\n * @param context - Docusaurus context\n * @param options - Plugin options\n * @param options.glossaryPath - Path to glossary JSON file (default: 'glossary/glossary.json')\n * @param options.routePath - Route path for glossary page (default: '/glossary')\n * @param options.autoLinkTerms - Legacy option, kept for compatibility but no longer used (configure remark plugin manually instead)\n * @returns Plugin object\n */\nexport default function glossaryPlugin(\n context: LoadContext,\n options: GlossaryPluginOptions = {}\n): Plugin {\n const { glossaryPath = 'glossary/glossary.json', routePath = '/glossary' } = options;\n\n return {\n name: 'docusaurus-plugin-glossary',\n\n getClientModules() {\n return [path.resolve(currentDir, './client/index.js')];\n },\n\n async loadContent() {\n // Load glossary terms from JSON file\n const glossaryFilePath = path.resolve(context.siteDir, glossaryPath);\n\n if (await fs.pathExists(glossaryFilePath)) {\n try {\n const rawData = await fs.readJson(glossaryFilePath);\n\n // Validate glossary data structure\n const validationResult = validateGlossaryData(rawData, { throwOnError: false });\n\n if (!validationResult.valid) {\n console.warn(\n `[glossary-plugin] Glossary file has validation errors at ${glossaryFilePath}:`\n );\n validationResult.errors.forEach(err => {\n console.warn(` - [${err.field}] ${err.message}`);\n });\n console.warn('[glossary-plugin] Proceeding with valid terms only.');\n }\n\n return validationResult.data;\n } catch (error) {\n if (error instanceof GlossaryValidationError) {\n throw error;\n }\n // JSON parsing error\n throw new Error(\n `Failed to parse glossary file at ${glossaryFilePath}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n console.warn(`Glossary file not found at ${glossaryFilePath}. Using empty glossary.`);\n return { terms: [] };\n },\n\n async contentLoaded({ content, actions }) {\n const { createData, addRoute, setGlobalData } = actions;\n const glossaryContent = content as GlossaryData;\n\n // Create data file that can be imported by components\n const glossaryDataPath = await createData(\n 'glossary-data.json',\n JSON.stringify(glossaryContent)\n );\n\n // Create a data file for the remark plugin to access glossary terms\n await createData(\n 'remark-glossary-data.json',\n JSON.stringify({\n terms: glossaryContent.terms || [],\n routePath: routePath,\n })\n );\n\n // Add glossary page route\n addRoute({\n path: routePath,\n component: path.join(currentDir, 'components/GlossaryPage.js'),\n exact: true,\n modules: {\n glossaryData: glossaryDataPath,\n },\n });\n\n // Expose global data for runtime lookups (used by GlossaryTerm)\n setGlobalData({\n terms: glossaryContent.terms || [],\n routePath,\n });\n },\n\n getThemePath() {\n return path.resolve(currentDir, './theme');\n },\n\n getPathsToWatch() {\n return [path.resolve(context.siteDir, glossaryPath)];\n },\n\n async postBuild() {\n // You can add any post-build steps here if needed\n console.log('Glossary plugin: Build completed');\n },\n };\n}\n\n// Export remark plugin factory for use in markdown configuration\nexport const remarkPlugin = remarkGlossaryTerms;\n\n// Export cache clearing utility\nexport { clearGlossaryCache } from './remark/glossary-terms.js';\n\n// Export validation utilities\nexport {\n validateGlossaryData,\n GlossaryValidationError,\n formatValidationErrors,\n type ValidationError,\n type ValidationResult,\n} from './validation.js';\n\n/**\n * Helper function to get the configured remark plugin\n * This can be used in docusaurus.config.js markdown configuration\n *\n * @param pluginOptions - Plugin options from docusaurus.config.js\n * @param context - Context with siteDir\n * @returns Configured remark plugin\n */\nexport function getRemarkPlugin(\n pluginOptions: GlossaryPluginOptions,\n context?: { siteDir?: string }\n): [typeof remarkGlossaryTerms, { glossaryPath: string; routePath: string; siteDir?: string }] {\n const { glossaryPath = 'glossary/glossary.json', routePath = '/glossary' } = pluginOptions;\n\n const siteDir = context?.siteDir;\n\n return [\n remarkGlossaryTerms,\n {\n glossaryPath,\n routePath,\n siteDir,\n },\n ];\n}\n"],"mappings":";;;;;;;;;AACA,OAAO,UAAU;AAEjB,SAAS,qBAAqB;AAC9B,OAAO,QAAQ;AAEf,OAAO,8BAA8B;AAKrC,IAAM,kBAAkB,cAAc,YAAY,GAAG;AACrD,IAAM,aAAa,KAAK,QAAQ,eAAe;AAG/C,yBAAyB,UAAU;AAwFpB,SAAR,eACL,SACA,UAAiC,CAAC,GAC1B;AACR,QAAM,EAAE,eAAe,0BAA0B,YAAY,YAAY,IAAI;AAE7E,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,mBAAmB;AACjB,aAAO,CAAC,KAAK,QAAQ,YAAY,mBAAmB,CAAC;AAAA,IACvD;AAAA,IAEA,MAAM,cAAc;AAElB,YAAM,mBAAmB,KAAK,QAAQ,QAAQ,SAAS,YAAY;AAEnE,UAAI,MAAM,GAAG,WAAW,gBAAgB,GAAG;AACzC,YAAI;AACF,gBAAM,UAAU,MAAM,GAAG,SAAS,gBAAgB;AAGlD,gBAAM,mBAAmB,qBAAqB,SAAS,EAAE,cAAc,MAAM,CAAC;AAE9E,cAAI,CAAC,iBAAiB,OAAO;AAC3B,oBAAQ;AAAA,cACN,4DAA4D,gBAAgB;AAAA,YAC9E;AACA,6BAAiB,OAAO,QAAQ,SAAO;AACrC,sBAAQ,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,OAAO,EAAE;AAAA,YAClD,CAAC;AACD,oBAAQ,KAAK,qDAAqD;AAAA,UACpE;AAEA,iBAAO,iBAAiB;AAAA,QAC1B,SAAS,OAAO;AACd,cAAI,iBAAiB,yBAAyB;AAC5C,kBAAM;AAAA,UACR;AAEA,gBAAM,IAAI;AAAA,YACR,oCAAoC,gBAAgB,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UACjH;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,KAAK,8BAA8B,gBAAgB,yBAAyB;AACpF,aAAO,EAAE,OAAO,CAAC,EAAE;AAAA,IACrB;AAAA,IAEA,MAAM,cAAc,EAAE,SAAS,QAAQ,GAAG;AACxC,YAAM,EAAE,YAAY,UAAU,cAAc,IAAI;AAChD,YAAM,kBAAkB;AAGxB,YAAM,mBAAmB,MAAM;AAAA,QAC7B;AAAA,QACA,KAAK,UAAU,eAAe;AAAA,MAChC;AAGA,YAAM;AAAA,QACJ;AAAA,QACA,KAAK,UAAU;AAAA,UACb,OAAO,gBAAgB,SAAS,CAAC;AAAA,UACjC;AAAA,QACF,CAAC;AAAA,MACH;AAGA,eAAS;AAAA,QACP,MAAM;AAAA,QACN,WAAW,KAAK,KAAK,YAAY,4BAA4B;AAAA,QAC7D,OAAO;AAAA,QACP,SAAS;AAAA,UACP,cAAc;AAAA,QAChB;AAAA,MACF,CAAC;AAGD,oBAAc;AAAA,QACZ,OAAO,gBAAgB,SAAS,CAAC;AAAA,QACjC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,eAAe;AACb,aAAO,KAAK,QAAQ,YAAY,SAAS;AAAA,IAC3C;AAAA,IAEA,kBAAkB;AAChB,aAAO,CAAC,KAAK,QAAQ,QAAQ,SAAS,YAAY,CAAC;AAAA,IACrD;AAAA,IAEA,MAAM,YAAY;AAEhB,cAAQ,IAAI,kCAAkC;AAAA,IAChD;AAAA,EACF;AACF;AAGO,IAAM,eAAe;AAsBrB,SAAS,gBACd,eACA,SAC6F;AAC7F,QAAM,EAAE,eAAe,0BAA0B,YAAY,YAAY,IAAI;AAE7E,QAAM,UAAU,SAAS;AAEzB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// src/validation.ts
|
|
2
|
+
function validateTerm(term, index) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
const prefix = `terms[${index}]`;
|
|
5
|
+
if (term === null || term === void 0) {
|
|
6
|
+
errors.push({
|
|
7
|
+
field: prefix,
|
|
8
|
+
message: "Term cannot be null or undefined",
|
|
9
|
+
value: term
|
|
10
|
+
});
|
|
11
|
+
return errors;
|
|
12
|
+
}
|
|
13
|
+
if (typeof term !== "object") {
|
|
14
|
+
errors.push({
|
|
15
|
+
field: prefix,
|
|
16
|
+
message: `Term must be an object, got ${typeof term}`,
|
|
17
|
+
value: term
|
|
18
|
+
});
|
|
19
|
+
return errors;
|
|
20
|
+
}
|
|
21
|
+
const termObj = term;
|
|
22
|
+
if (!("term" in termObj)) {
|
|
23
|
+
errors.push({
|
|
24
|
+
field: `${prefix}.term`,
|
|
25
|
+
message: 'Missing required field "term"'
|
|
26
|
+
});
|
|
27
|
+
} else if (typeof termObj.term !== "string") {
|
|
28
|
+
errors.push({
|
|
29
|
+
field: `${prefix}.term`,
|
|
30
|
+
message: `Field "term" must be a string, got ${typeof termObj.term}`,
|
|
31
|
+
value: termObj.term
|
|
32
|
+
});
|
|
33
|
+
} else if (termObj.term.trim() === "") {
|
|
34
|
+
errors.push({
|
|
35
|
+
field: `${prefix}.term`,
|
|
36
|
+
message: 'Field "term" cannot be empty',
|
|
37
|
+
value: termObj.term
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (!("definition" in termObj)) {
|
|
41
|
+
errors.push({
|
|
42
|
+
field: `${prefix}.definition`,
|
|
43
|
+
message: 'Missing required field "definition"'
|
|
44
|
+
});
|
|
45
|
+
} else if (typeof termObj.definition !== "string") {
|
|
46
|
+
errors.push({
|
|
47
|
+
field: `${prefix}.definition`,
|
|
48
|
+
message: `Field "definition" must be a string, got ${typeof termObj.definition}`,
|
|
49
|
+
value: termObj.definition
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if ("abbreviation" in termObj && termObj.abbreviation !== void 0) {
|
|
53
|
+
if (typeof termObj.abbreviation !== "string") {
|
|
54
|
+
errors.push({
|
|
55
|
+
field: `${prefix}.abbreviation`,
|
|
56
|
+
message: `Field "abbreviation" must be a string, got ${typeof termObj.abbreviation}`,
|
|
57
|
+
value: termObj.abbreviation
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if ("relatedTerms" in termObj && termObj.relatedTerms !== void 0) {
|
|
62
|
+
if (!Array.isArray(termObj.relatedTerms)) {
|
|
63
|
+
errors.push({
|
|
64
|
+
field: `${prefix}.relatedTerms`,
|
|
65
|
+
message: `Field "relatedTerms" must be an array, got ${typeof termObj.relatedTerms}`,
|
|
66
|
+
value: termObj.relatedTerms
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
termObj.relatedTerms.forEach((relatedTerm, relatedIndex) => {
|
|
70
|
+
if (typeof relatedTerm !== "string") {
|
|
71
|
+
errors.push({
|
|
72
|
+
field: `${prefix}.relatedTerms[${relatedIndex}]`,
|
|
73
|
+
message: `Related term must be a string, got ${typeof relatedTerm}`,
|
|
74
|
+
value: relatedTerm
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if ("id" in termObj && termObj.id !== void 0) {
|
|
81
|
+
if (typeof termObj.id !== "string") {
|
|
82
|
+
errors.push({
|
|
83
|
+
field: `${prefix}.id`,
|
|
84
|
+
message: `Field "id" must be a string, got ${typeof termObj.id}`,
|
|
85
|
+
value: termObj.id
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if ("autoLink" in termObj && termObj.autoLink !== void 0) {
|
|
90
|
+
if (typeof termObj.autoLink !== "boolean") {
|
|
91
|
+
errors.push({
|
|
92
|
+
field: `${prefix}.autoLink`,
|
|
93
|
+
message: `Field "autoLink" must be a boolean, got ${typeof termObj.autoLink}`,
|
|
94
|
+
value: termObj.autoLink
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if ("aliases" in termObj && termObj.aliases !== void 0) {
|
|
99
|
+
if (!Array.isArray(termObj.aliases)) {
|
|
100
|
+
errors.push({
|
|
101
|
+
field: `${prefix}.aliases`,
|
|
102
|
+
message: `Field "aliases" must be an array, got ${typeof termObj.aliases}`,
|
|
103
|
+
value: termObj.aliases
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
termObj.aliases.forEach((alias, aliasIndex) => {
|
|
107
|
+
if (typeof alias !== "string") {
|
|
108
|
+
errors.push({
|
|
109
|
+
field: `${prefix}.aliases[${aliasIndex}]`,
|
|
110
|
+
message: `Alias must be a string, got ${typeof alias}`,
|
|
111
|
+
value: alias
|
|
112
|
+
});
|
|
113
|
+
} else if (alias.trim() === "") {
|
|
114
|
+
errors.push({
|
|
115
|
+
field: `${prefix}.aliases[${aliasIndex}]`,
|
|
116
|
+
message: "Alias cannot be empty",
|
|
117
|
+
value: alias
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return errors;
|
|
124
|
+
}
|
|
125
|
+
function validateGlossaryData(data, options = {}) {
|
|
126
|
+
const { throwOnError = true } = options;
|
|
127
|
+
const errors = [];
|
|
128
|
+
if (data === null || data === void 0) {
|
|
129
|
+
errors.push({
|
|
130
|
+
field: "root",
|
|
131
|
+
message: "Glossary data cannot be null or undefined",
|
|
132
|
+
value: data
|
|
133
|
+
});
|
|
134
|
+
if (throwOnError && errors.length > 0) {
|
|
135
|
+
throw new GlossaryValidationError(errors);
|
|
136
|
+
}
|
|
137
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
138
|
+
}
|
|
139
|
+
if (typeof data !== "object") {
|
|
140
|
+
errors.push({
|
|
141
|
+
field: "root",
|
|
142
|
+
message: `Glossary data must be an object, got ${typeof data}`,
|
|
143
|
+
value: data
|
|
144
|
+
});
|
|
145
|
+
if (throwOnError && errors.length > 0) {
|
|
146
|
+
throw new GlossaryValidationError(errors);
|
|
147
|
+
}
|
|
148
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
149
|
+
}
|
|
150
|
+
const glossaryData = data;
|
|
151
|
+
if ("title" in glossaryData && typeof glossaryData.title !== "string") {
|
|
152
|
+
errors.push({
|
|
153
|
+
field: "title",
|
|
154
|
+
message: "The title property in the GlossaryData must be a string."
|
|
155
|
+
});
|
|
156
|
+
if (throwOnError && errors.length > 0) {
|
|
157
|
+
throw new GlossaryValidationError(errors);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const validTitle = glossaryData.title;
|
|
161
|
+
if ("description" in glossaryData && typeof glossaryData.description !== "string") {
|
|
162
|
+
errors.push({
|
|
163
|
+
field: "description",
|
|
164
|
+
message: "The description property in the GlossaryData must be a string."
|
|
165
|
+
});
|
|
166
|
+
if (throwOnError && errors.length > 0) {
|
|
167
|
+
throw new GlossaryValidationError(errors);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const validDescription = glossaryData.description;
|
|
171
|
+
if (!("terms" in glossaryData)) {
|
|
172
|
+
errors.push({
|
|
173
|
+
field: "terms",
|
|
174
|
+
message: 'Glossary data must contain a "terms" array'
|
|
175
|
+
});
|
|
176
|
+
if (throwOnError && errors.length > 0) {
|
|
177
|
+
throw new GlossaryValidationError(errors);
|
|
178
|
+
}
|
|
179
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
180
|
+
}
|
|
181
|
+
if (!Array.isArray(glossaryData.terms)) {
|
|
182
|
+
errors.push({
|
|
183
|
+
field: "terms",
|
|
184
|
+
message: `Field "terms" must be an array, got ${typeof glossaryData.terms}`,
|
|
185
|
+
value: glossaryData.terms
|
|
186
|
+
});
|
|
187
|
+
if (throwOnError && errors.length > 0) {
|
|
188
|
+
throw new GlossaryValidationError(errors);
|
|
189
|
+
}
|
|
190
|
+
return { valid: false, errors, data: { terms: [] } };
|
|
191
|
+
}
|
|
192
|
+
const validTerms = [];
|
|
193
|
+
glossaryData.terms.forEach((term, index) => {
|
|
194
|
+
const termErrors = validateTerm(term, index);
|
|
195
|
+
if (termErrors.length > 0) {
|
|
196
|
+
errors.push(...termErrors);
|
|
197
|
+
} else {
|
|
198
|
+
validTerms.push(term);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const termNames = /* @__PURE__ */ new Map();
|
|
202
|
+
validTerms.forEach((term, index) => {
|
|
203
|
+
const lowerName = term.term.toLowerCase();
|
|
204
|
+
if (termNames.has(lowerName)) {
|
|
205
|
+
errors.push({
|
|
206
|
+
field: `terms[${index}].term`,
|
|
207
|
+
message: `Duplicate term "${term.term}" (first occurrence at index ${termNames.get(lowerName)})`,
|
|
208
|
+
value: term.term
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
termNames.set(lowerName, index);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
if (throwOnError && errors.length > 0) {
|
|
215
|
+
throw new GlossaryValidationError(errors);
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
valid: errors.length === 0,
|
|
219
|
+
errors,
|
|
220
|
+
data: { title: validTitle, description: validDescription, terms: validTerms }
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
var GlossaryValidationError = class _GlossaryValidationError extends Error {
|
|
224
|
+
constructor(errors) {
|
|
225
|
+
const message = formatValidationErrors(errors);
|
|
226
|
+
super(message);
|
|
227
|
+
this.name = "GlossaryValidationError";
|
|
228
|
+
this.errors = errors;
|
|
229
|
+
if (Error.captureStackTrace) {
|
|
230
|
+
Error.captureStackTrace(this, _GlossaryValidationError);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
function formatValidationErrors(errors) {
|
|
235
|
+
if (errors.length === 0) {
|
|
236
|
+
return "No validation errors";
|
|
237
|
+
}
|
|
238
|
+
const header = `Glossary validation failed with ${errors.length} error${errors.length > 1 ? "s" : ""}:`;
|
|
239
|
+
const errorList = errors.map((err, index) => {
|
|
240
|
+
let msg = ` ${index + 1}. [${err.field}] ${err.message}`;
|
|
241
|
+
if (err.value !== void 0) {
|
|
242
|
+
const valueStr = typeof err.value === "object" ? JSON.stringify(err.value) : String(err.value);
|
|
243
|
+
const truncated = valueStr.length > 50 ? valueStr.substring(0, 50) + "..." : valueStr;
|
|
244
|
+
msg += ` (got: ${truncated})`;
|
|
245
|
+
}
|
|
246
|
+
return msg;
|
|
247
|
+
}).join("\n");
|
|
248
|
+
return `${header}
|
|
249
|
+
${errorList}`;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export {
|
|
253
|
+
validateGlossaryData,
|
|
254
|
+
GlossaryValidationError,
|
|
255
|
+
formatValidationErrors
|
|
256
|
+
};
|
|
257
|
+
//# sourceMappingURL=chunk-7Z37JEHW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation.ts"],"sourcesContent":["import type { GlossaryData, GlossaryTerm } from './index.js';\n\nexport interface ValidationError {\n field: string;\n message: string;\n value?: unknown;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n data: GlossaryData;\n}\n\n/**\n * Validates a single glossary term object\n *\n * @param term - The term object to validate\n * @param index - The index in the terms array (for error messages)\n * @returns Array of validation errors (empty if valid)\n */\nfunction validateTerm(term: unknown, index: number): ValidationError[] {\n const errors: ValidationError[] = [];\n const prefix = `terms[${index}]`;\n\n if (term === null || term === undefined) {\n errors.push({\n field: prefix,\n message: 'Term cannot be null or undefined',\n value: term,\n });\n return errors;\n }\n\n if (typeof term !== 'object') {\n errors.push({\n field: prefix,\n message: `Term must be an object, got ${typeof term}`,\n value: term,\n });\n return errors;\n }\n\n const termObj = term as Record<string, unknown>;\n\n // Required: term (string)\n if (!('term' in termObj)) {\n errors.push({\n field: `${prefix}.term`,\n message: 'Missing required field \"term\"',\n });\n } else if (typeof termObj.term !== 'string') {\n errors.push({\n field: `${prefix}.term`,\n message: `Field \"term\" must be a string, got ${typeof termObj.term}`,\n value: termObj.term,\n });\n } else if (termObj.term.trim() === '') {\n errors.push({\n field: `${prefix}.term`,\n message: 'Field \"term\" cannot be empty',\n value: termObj.term,\n });\n }\n\n // Required: definition (string)\n if (!('definition' in termObj)) {\n errors.push({\n field: `${prefix}.definition`,\n message: 'Missing required field \"definition\"',\n });\n } else if (typeof termObj.definition !== 'string') {\n errors.push({\n field: `${prefix}.definition`,\n message: `Field \"definition\" must be a string, got ${typeof termObj.definition}`,\n value: termObj.definition,\n });\n }\n\n // Optional: abbreviation (string)\n if ('abbreviation' in termObj && termObj.abbreviation !== undefined) {\n if (typeof termObj.abbreviation !== 'string') {\n errors.push({\n field: `${prefix}.abbreviation`,\n message: `Field \"abbreviation\" must be a string, got ${typeof termObj.abbreviation}`,\n value: termObj.abbreviation,\n });\n }\n }\n\n // Optional: relatedTerms (string[])\n if ('relatedTerms' in termObj && termObj.relatedTerms !== undefined) {\n if (!Array.isArray(termObj.relatedTerms)) {\n errors.push({\n field: `${prefix}.relatedTerms`,\n message: `Field \"relatedTerms\" must be an array, got ${typeof termObj.relatedTerms}`,\n value: termObj.relatedTerms,\n });\n } else {\n termObj.relatedTerms.forEach((relatedTerm, relatedIndex) => {\n if (typeof relatedTerm !== 'string') {\n errors.push({\n field: `${prefix}.relatedTerms[${relatedIndex}]`,\n message: `Related term must be a string, got ${typeof relatedTerm}`,\n value: relatedTerm,\n });\n }\n });\n }\n }\n\n // Optional: id (string)\n if ('id' in termObj && termObj.id !== undefined) {\n if (typeof termObj.id !== 'string') {\n errors.push({\n field: `${prefix}.id`,\n message: `Field \"id\" must be a string, got ${typeof termObj.id}`,\n value: termObj.id,\n });\n }\n }\n\n // Optional: autoLink (boolean)\n if ('autoLink' in termObj && termObj.autoLink !== undefined) {\n if (typeof termObj.autoLink !== 'boolean') {\n errors.push({\n field: `${prefix}.autoLink`,\n message: `Field \"autoLink\" must be a boolean, got ${typeof termObj.autoLink}`,\n value: termObj.autoLink,\n });\n }\n }\n\n // Optional: aliases (string[])\n if ('aliases' in termObj && termObj.aliases !== undefined) {\n if (!Array.isArray(termObj.aliases)) {\n errors.push({\n field: `${prefix}.aliases`,\n message: `Field \"aliases\" must be an array, got ${typeof termObj.aliases}`,\n value: termObj.aliases,\n });\n } else {\n termObj.aliases.forEach((alias, aliasIndex) => {\n if (typeof alias !== 'string') {\n errors.push({\n field: `${prefix}.aliases[${aliasIndex}]`,\n message: `Alias must be a string, got ${typeof alias}`,\n value: alias,\n });\n } else if (alias.trim() === '') {\n errors.push({\n field: `${prefix}.aliases[${aliasIndex}]`,\n message: 'Alias cannot be empty',\n value: alias,\n });\n }\n });\n }\n }\n\n return errors;\n}\n\n/**\n * Validates glossary data structure\n *\n * Ensures the glossary data conforms to the expected schema:\n * - Must be an object with a \"terms\" array\n * - Each term must have \"term\" (string) and \"definition\" (string)\n * - Optional fields: abbreviation (string), relatedTerms (string[]), id (string)\n *\n * @param data - The data to validate\n * @param options - Validation options\n * @param options.throwOnError - If true, throws an error on validation failure (default: true)\n * @returns Validation result with errors and sanitized data\n * @throws Error if data is invalid and throwOnError is true\n */\nexport function validateGlossaryData(\n data: unknown,\n options: { throwOnError?: boolean } = {}\n): ValidationResult {\n const { throwOnError = true } = options;\n const errors: ValidationError[] = [];\n\n // Check if data is null or undefined\n if (data === null || data === undefined) {\n errors.push({\n field: 'root',\n message: 'Glossary data cannot be null or undefined',\n value: data,\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n\n return { valid: false, errors, data: { terms: [] } };\n }\n\n // Check if data is an object\n if (typeof data !== 'object') {\n errors.push({\n field: 'root',\n message: `Glossary data must be an object, got ${typeof data}`,\n value: data,\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n\n return { valid: false, errors, data: { terms: [] } };\n }\n\n const glossaryData = data as Record<string, unknown>;\n\n //Check for title\n if ('title' in glossaryData && typeof glossaryData.title !== 'string') {\n errors.push({\n field: 'title',\n message: 'The title property in the GlossaryData must be a string.',\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n }\n const validTitle = glossaryData.title as string;\n\n if ('description' in glossaryData && typeof glossaryData.description !== 'string') {\n errors.push({\n field: 'description',\n message: 'The description property in the GlossaryData must be a string.',\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n }\n\n const validDescription = glossaryData.description as string;\n\n if (!('terms' in glossaryData)) {\n // Check for terms array\n errors.push({\n field: 'terms',\n message: 'Glossary data must contain a \"terms\" array',\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n\n return { valid: false, errors, data: { terms: [] } };\n }\n\n if (!Array.isArray(glossaryData.terms)) {\n errors.push({\n field: 'terms',\n message: `Field \"terms\" must be an array, got ${typeof glossaryData.terms}`,\n value: glossaryData.terms,\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n\n return { valid: false, errors, data: { terms: [] } };\n }\n\n // Validate each term\n const validTerms: GlossaryTerm[] = [];\n glossaryData.terms.forEach((term, index) => {\n const termErrors = validateTerm(term, index);\n if (termErrors.length > 0) {\n errors.push(...termErrors);\n } else {\n // Term is valid, add to valid terms\n validTerms.push(term as GlossaryTerm);\n }\n });\n\n // Check for duplicate terms\n const termNames = new Map<string, number>();\n validTerms.forEach((term, index) => {\n const lowerName = term.term.toLowerCase();\n if (termNames.has(lowerName)) {\n errors.push({\n field: `terms[${index}].term`,\n message: `Duplicate term \"${term.term}\" (first occurrence at index ${termNames.get(lowerName)})`,\n value: term.term,\n });\n } else {\n termNames.set(lowerName, index);\n }\n });\n\n if (throwOnError && errors.length > 0) {\n throw new GlossaryValidationError(errors);\n }\n\n return {\n valid: errors.length === 0,\n errors,\n data: { title: validTitle, description: validDescription, terms: validTerms },\n };\n}\n\n/**\n * Custom error class for glossary validation errors\n * Provides detailed error messages for debugging\n */\nexport class GlossaryValidationError extends Error {\n public readonly errors: ValidationError[];\n\n constructor(errors: ValidationError[]) {\n const message = formatValidationErrors(errors);\n super(message);\n this.name = 'GlossaryValidationError';\n this.errors = errors;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n // @ts-ignore\n if (Error.captureStackTrace) {\n // @ts-ignore\n Error.captureStackTrace(this, GlossaryValidationError);\n }\n }\n}\n\n/**\n * Formats validation errors into a readable string\n *\n * @param errors - Array of validation errors\n * @returns Formatted error message\n */\nexport function formatValidationErrors(errors: ValidationError[]): string {\n if (errors.length === 0) {\n return 'No validation errors';\n }\n\n const header = `Glossary validation failed with ${errors.length} error${errors.length > 1 ? 's' : ''}:`;\n const errorList = errors\n .map((err, index) => {\n let msg = ` ${index + 1}. [${err.field}] ${err.message}`;\n if (err.value !== undefined) {\n const valueStr =\n typeof err.value === 'object' ? JSON.stringify(err.value) : String(err.value);\n // Truncate long values\n const truncated = valueStr.length > 50 ? valueStr.substring(0, 50) + '...' : valueStr;\n msg += ` (got: ${truncated})`;\n }\n return msg;\n })\n .join('\\n');\n\n return `${header}\\n${errorList}`;\n}\n"],"mappings":"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","names":[]}
|