@rspress/plugin-api-docgen 2.0.0-beta.2 → 2.0.0-beta.21
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +49 -50
- package/index.d.ts +2 -1
- package/package.json +14 -12
- package/static/global-components/API.tsx +95 -2
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import type { CompilerOptions } from 'typescript';
|
2
2
|
import type { ParserOptions } from 'react-docgen-typescript';
|
3
|
-
import type { RspressPlugin } from '
|
3
|
+
import type { RspressPlugin } from 'rspress/core';
|
4
4
|
|
5
5
|
declare type ApiParseTool = 'documentation' | 'react-docgen-typescript';
|
6
6
|
|
package/dist/index.js
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
6
|
-
|
7
|
-
__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(__dirname, '..');
|
1
|
+
import node_fs from "node:fs";
|
2
|
+
import node_path from "node:path";
|
3
|
+
import { RSPRESS_TEMP_DIR, logger } from "rspress/core";
|
4
|
+
import chokidar from "chokidar";
|
5
|
+
import { withCompilerOptions, withCustomConfig, withDefaultConfig } from "react-docgen-typescript";
|
6
|
+
node_path.join(__dirname, '..');
|
8
7
|
const apiDocMap = {};
|
9
8
|
const locales = {
|
10
9
|
zh: {
|
11
|
-
copy:
|
12
|
-
copied:
|
13
|
-
expand:
|
14
|
-
collapse:
|
15
|
-
className:
|
16
|
-
style:
|
17
|
-
children:
|
18
|
-
disabled:
|
19
|
-
required:
|
20
|
-
property:
|
21
|
-
description:
|
22
|
-
type:
|
23
|
-
defaultValue:
|
24
|
-
overview:
|
10
|
+
copy: "\u590D\u5236",
|
11
|
+
copied: "\u590D\u5236\u6210\u529F",
|
12
|
+
expand: "\u5C55\u5F00\u4EE3\u7801",
|
13
|
+
collapse: "\u6536\u8D77\u4EE3\u7801",
|
14
|
+
className: "\u8282\u70B9\u7C7B\u540D",
|
15
|
+
style: "\u8282\u70B9\u6837\u5F0F",
|
16
|
+
children: "\u5B50\u8282\u70B9",
|
17
|
+
disabled: "\u662F\u5426\u7981\u7528",
|
18
|
+
required: "\u5FC5\u586B",
|
19
|
+
property: "\u5C5E\u6027",
|
20
|
+
description: "\u8BF4\u660E",
|
21
|
+
type: "\u7C7B\u578B",
|
22
|
+
defaultValue: "\u9ED8\u8BA4\u503C",
|
23
|
+
overview: "\u6982\u89C8"
|
25
24
|
},
|
26
25
|
en: {
|
27
26
|
copy: 'Copy',
|
@@ -40,20 +39,20 @@ const locales = {
|
|
40
39
|
overview: 'Overview'
|
41
40
|
},
|
42
41
|
ru: {
|
43
|
-
copy:
|
44
|
-
copied:
|
45
|
-
expand:
|
46
|
-
collapse:
|
47
|
-
className:
|
48
|
-
style:
|
49
|
-
children:
|
50
|
-
disabled:
|
51
|
-
required:
|
52
|
-
property:
|
53
|
-
description:
|
54
|
-
type:
|
55
|
-
defaultValue:
|
56
|
-
overview:
|
42
|
+
copy: "\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
|
43
|
+
copied: "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E \u0443\u0441\u043F\u0435\u0448\u043D\u043E",
|
44
|
+
expand: "\u0420\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u0442\u044C \u043A\u043E\u0434",
|
45
|
+
collapse: "\u0421\u0432\u0435\u0440\u043D\u0443\u0442\u044C \u043A\u043E\u0434",
|
46
|
+
className: "\u0418\u043C\u044F \u043A\u043B\u0430\u0441\u0441\u0430 \u0443\u0437\u043B\u0430",
|
47
|
+
style: "\u0421\u0442\u0438\u043B\u044C \u0443\u0437\u043B\u0430",
|
48
|
+
children: "\u0414\u043E\u0447\u0435\u0440\u043D\u0438\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B",
|
49
|
+
disabled: "\u041E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u043E",
|
50
|
+
required: "\u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
|
51
|
+
property: "\u0421\u0432\u043E\u0439\u0441\u0442\u0432\u043E",
|
52
|
+
description: "\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435",
|
53
|
+
type: "\u0422\u0438\u043F",
|
54
|
+
defaultValue: "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E",
|
55
|
+
overview: "\u041E\u0431\u0437\u043E\u0440"
|
57
56
|
}
|
58
57
|
};
|
59
58
|
const isToolEntries = (obj)=>!!obj.documentation || !!obj["react-docgen-typescript"];
|
@@ -62,7 +61,7 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
62
61
|
const genApiDoc = async (entry, tool)=>{
|
63
62
|
if (0 === Object.keys(entry).length) return;
|
64
63
|
await Promise.all(Object.entries(entry).map(async ([key, value])=>{
|
65
|
-
const moduleSourceFilePath =
|
64
|
+
const moduleSourceFilePath = node_path.resolve(appDir, value);
|
66
65
|
watchFileMap[moduleSourceFilePath] = {
|
67
66
|
apiParseTool,
|
68
67
|
moduleName: key
|
@@ -91,22 +90,22 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
91
90
|
},
|
92
91
|
...restOptions
|
93
92
|
};
|
94
|
-
let fileParser =
|
95
|
-
if (tsconfigPath?.[key]) fileParser =
|
96
|
-
else if (compilerOptions?.[key]) fileParser =
|
93
|
+
let fileParser = withDefaultConfig(parserOpts);
|
94
|
+
if (tsconfigPath?.[key]) fileParser = withCustomConfig(tsconfigPath[key], parserOpts);
|
95
|
+
else if (compilerOptions?.[key]) fileParser = withCompilerOptions(compilerOptions[key], parserOpts);
|
97
96
|
const componentDoc = fileParser.parse(moduleSourceFilePath);
|
98
|
-
if (0 === componentDoc.length)
|
97
|
+
if (0 === componentDoc.length) logger.warn('[module-doc-plugin]', `Unable to parse API document in ${moduleSourceFilePath}`);
|
99
98
|
if (languages.length > 0) languages.forEach((language)=>{
|
100
99
|
apiDocMap[`${key}-${language}`] = generateTable(componentDoc, language);
|
101
100
|
});
|
102
101
|
else apiDocMap[key] = generateTable(componentDoc, 'en');
|
103
102
|
}
|
104
103
|
} catch (e) {
|
105
|
-
if (e instanceof Error)
|
104
|
+
if (e instanceof Error) logger.error('[module-doc-plugin]', 'Generate API table error:\n', e);
|
106
105
|
}
|
107
106
|
}));
|
108
107
|
};
|
109
|
-
|
108
|
+
logger.info('[module-doc-plugin]', 'Start to generate API table...');
|
110
109
|
if (isToolEntries(entries)) {
|
111
110
|
const reactEntries = entries["react-docgen-typescript"];
|
112
111
|
const documentationEntries = entries.documentation;
|
@@ -116,7 +115,7 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
116
115
|
]);
|
117
116
|
} else await genApiDoc(entries, apiParseTool);
|
118
117
|
if (!isProd) {
|
119
|
-
const watcher =
|
118
|
+
const watcher = chokidar.watch(Object.keys(watchFileMap), {
|
120
119
|
ignoreInitial: true,
|
121
120
|
ignorePermissionErrors: true,
|
122
121
|
ignored: [
|
@@ -127,12 +126,12 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
127
126
|
watcher.on('change', (changed)=>{
|
128
127
|
if (isUpdate) return;
|
129
128
|
isUpdate = true;
|
130
|
-
|
129
|
+
logger.info('[module-doc-plugin]', 'updating API');
|
131
130
|
const watchFileInfo = watchFileMap[changed];
|
132
131
|
if (watchFileInfo) {
|
133
132
|
const { apiParseTool, moduleName } = watchFileInfo;
|
134
133
|
const updateSiteData = ()=>{
|
135
|
-
const siteDataPath =
|
134
|
+
const siteDataPath = node_path.join(process.cwd(), 'node_modules', RSPRESS_TEMP_DIR, 'runtime', 'virtual-site-data.mjs');
|
136
135
|
import(siteDataPath).then((siteData)=>{
|
137
136
|
const data = {
|
138
137
|
...siteData.default
|
@@ -140,7 +139,7 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
140
139
|
data.pages.forEach((page)=>{
|
141
140
|
page.apiDocMap = apiDocMap;
|
142
141
|
});
|
143
|
-
|
142
|
+
node_fs.writeFileSync(siteDataPath, `export default ${JSON.stringify(data)}`);
|
144
143
|
isUpdate = false;
|
145
144
|
});
|
146
145
|
};
|
@@ -150,7 +149,7 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
150
149
|
}
|
151
150
|
});
|
152
151
|
}
|
153
|
-
|
152
|
+
logger.success('[module-doc-plugin]', 'Generate API table successfully!');
|
154
153
|
};
|
155
154
|
function generateTable(componentDoc, language) {
|
156
155
|
return componentDoc.map((param)=>{
|
@@ -226,14 +225,14 @@ function pluginApiDocgen(options) {
|
|
226
225
|
const apiCompRegExp = /(<API\s+moduleName=['"](\S+)['"]\s*(.*)?\/>)|(<API\s+moduleName=['"](\S+)['"]\s*(.*)?>(.*)?<\/API>)/;
|
227
226
|
await Promise.all(pages.map(async (page)=>{
|
228
227
|
const { _filepath, lang } = page;
|
229
|
-
let content = await
|
228
|
+
let content = await node_fs.promises.readFile(_filepath, 'utf-8');
|
230
229
|
let matchResult = apiCompRegExp.exec(content);
|
231
230
|
if (!matchResult) return;
|
232
231
|
while(null !== matchResult){
|
233
232
|
const matchContent = matchResult[0];
|
234
233
|
const moduleName = matchResult[2] ?? matchResult[5] ?? '';
|
235
234
|
const apiDoc = apiDocMap[moduleName] ?? apiDocMap[`${moduleName}-${lang ? lang : 'en'}`] ?? '';
|
236
|
-
if (matchContent && !apiDoc)
|
235
|
+
if (matchContent && !apiDoc) logger.warn(`No api doc found for module: ${moduleName} in lang: ${lang ?? 'en'}`);
|
237
236
|
content = content.replace(matchContent, apiDoc);
|
238
237
|
matchResult = apiCompRegExp.exec(content);
|
239
238
|
}
|
@@ -247,7 +246,7 @@ function pluginApiDocgen(options) {
|
|
247
246
|
},
|
248
247
|
markdown: {
|
249
248
|
globalComponents: [
|
250
|
-
|
249
|
+
node_path.join(__dirname, '..', 'static', 'global-components', 'API.tsx')
|
251
250
|
]
|
252
251
|
}
|
253
252
|
};
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rspress/plugin-api-docgen",
|
3
|
-
"version": "2.0.0-beta.
|
3
|
+
"version": "2.0.0-beta.21",
|
4
4
|
"description": "A plugin for rspress to generate api doc.",
|
5
5
|
"bugs": "https://github.com/web-infra-dev/rspress/issues",
|
6
6
|
"repository": {
|
@@ -12,11 +12,11 @@
|
|
12
12
|
"type": "module",
|
13
13
|
"exports": {
|
14
14
|
".": {
|
15
|
-
"types": "./index.d.ts",
|
15
|
+
"types": "./dist/index.d.ts",
|
16
16
|
"default": "./dist/index.js"
|
17
17
|
}
|
18
18
|
},
|
19
|
-
"types": "./index.d.ts",
|
19
|
+
"types": "./dist/index.d.ts",
|
20
20
|
"files": [
|
21
21
|
"dist",
|
22
22
|
"static",
|
@@ -25,27 +25,29 @@
|
|
25
25
|
"dependencies": {
|
26
26
|
"chokidar": "^3.6.0",
|
27
27
|
"documentation": "14.0.3",
|
28
|
-
"
|
28
|
+
"github-slugger": "^2.0.0",
|
29
|
+
"react-docgen-typescript": "2.4.0",
|
29
30
|
"react-markdown": "^10.1.0",
|
30
31
|
"remark-gfm": "^4.0.1",
|
31
32
|
"unified": "^11.0.5",
|
32
|
-
"
|
33
|
+
"unist-util-visit": "^5.0.0"
|
33
34
|
},
|
34
35
|
"devDependencies": {
|
35
|
-
"@microsoft/api-extractor": "^7.52.
|
36
|
-
"@rslib/core": "0.
|
36
|
+
"@microsoft/api-extractor": "^7.52.8",
|
37
|
+
"@rslib/core": "0.10.5",
|
38
|
+
"@types/hast": "^3.0.4",
|
37
39
|
"@types/mdast": "^4.0.4",
|
38
|
-
"@types/node": "^
|
39
|
-
"@types/react": "^
|
40
|
-
"@types/react-dom": "^
|
40
|
+
"@types/node": "^22.8.1",
|
41
|
+
"@types/react": "^19.1.8",
|
42
|
+
"@types/react-dom": "^19.1.6",
|
41
43
|
"react": "^19.1.0",
|
42
44
|
"react-dom": "^19.1.0",
|
43
45
|
"react-router-dom": "^6.29.0",
|
44
|
-
"rsbuild-plugin-publint": "^0.3.
|
46
|
+
"rsbuild-plugin-publint": "^0.3.2",
|
45
47
|
"typescript": "^5.8.2"
|
46
48
|
},
|
47
49
|
"peerDependencies": {
|
48
|
-
"
|
50
|
+
"rspress": "^2.0.0-beta.21",
|
49
51
|
"typescript": "^5.8.2"
|
50
52
|
},
|
51
53
|
"peerDependenciesMeta": {
|
@@ -1,10 +1,102 @@
|
|
1
1
|
/// <reference path="../../index.d.ts" />
|
2
2
|
|
3
|
-
|
4
|
-
import { getCustomMDXComponent } from '@
|
3
|
+
// @ts-ignore @theme is overridden by alias in @rspress/core
|
4
|
+
import { getCustomMDXComponent } from '@theme';
|
5
5
|
import ReactMarkdown from 'react-markdown';
|
6
6
|
import remarkGfm from 'remark-gfm';
|
7
|
+
import { useLang, usePageData } from 'rspress/runtime';
|
7
8
|
import './API.css';
|
9
|
+
import GithubSlugger from 'github-slugger';
|
10
|
+
import type { Content, Element, Root } from 'hast';
|
11
|
+
// biome-ignore lint/style/useImportType: <exact>
|
12
|
+
import React from 'react';
|
13
|
+
import type { Plugin } from 'unified';
|
14
|
+
import { visit } from 'unist-util-visit';
|
15
|
+
|
16
|
+
function headingRank(node: Root | Content): number | null {
|
17
|
+
const name =
|
18
|
+
(node && node.type === 'element' && node.tagName.toLowerCase()) || '';
|
19
|
+
const code =
|
20
|
+
name.length === 2 && name.charCodeAt(0) === 104 /* `h` */
|
21
|
+
? name.charCodeAt(1)
|
22
|
+
: 0;
|
23
|
+
return code > 48 /* `0` */ && code < 55 /* `7` */
|
24
|
+
? code - 48 /* `0` */
|
25
|
+
: null;
|
26
|
+
}
|
27
|
+
|
28
|
+
const rehypeHeaderAnchor: Plugin<[], Root> = () => {
|
29
|
+
const slugger = new GithubSlugger();
|
30
|
+
return tree => {
|
31
|
+
visit(tree, 'element', node => {
|
32
|
+
if (!headingRank(node)) {
|
33
|
+
return;
|
34
|
+
}
|
35
|
+
// generate id
|
36
|
+
|
37
|
+
if (!node.properties?.id) {
|
38
|
+
const text = collectHeaderText(node);
|
39
|
+
node.properties ??= {};
|
40
|
+
node.properties.id = slugger.slug(text.trim());
|
41
|
+
}
|
42
|
+
// apply to headings
|
43
|
+
node.children.unshift(create(node));
|
44
|
+
});
|
45
|
+
};
|
46
|
+
};
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Create an `a`.
|
50
|
+
*
|
51
|
+
* @param {Readonly<Element>} node
|
52
|
+
* Related heading.
|
53
|
+
* @returns {Element}
|
54
|
+
* Link.
|
55
|
+
*/
|
56
|
+
function create(node: Element): Element {
|
57
|
+
return {
|
58
|
+
type: 'element',
|
59
|
+
tagName: 'a',
|
60
|
+
properties: {
|
61
|
+
class: 'header-anchor',
|
62
|
+
ariaHidden: 'true',
|
63
|
+
href: `#${node.properties!.id}`,
|
64
|
+
},
|
65
|
+
children: [
|
66
|
+
{
|
67
|
+
type: 'text',
|
68
|
+
value: '#',
|
69
|
+
},
|
70
|
+
],
|
71
|
+
};
|
72
|
+
}
|
73
|
+
|
74
|
+
const extractTextAndId = (title?: string): string => {
|
75
|
+
if (!title) {
|
76
|
+
return '';
|
77
|
+
}
|
78
|
+
const text = title.trimEnd();
|
79
|
+
return text;
|
80
|
+
};
|
81
|
+
|
82
|
+
const collectHeaderText = (node: Element): string => {
|
83
|
+
let text = '';
|
84
|
+
node.children.forEach(child => {
|
85
|
+
if (child.type === 'text') {
|
86
|
+
const textPart = extractTextAndId(child.value);
|
87
|
+
child.value = textPart;
|
88
|
+
text += textPart;
|
89
|
+
}
|
90
|
+
if (child.type === 'element') {
|
91
|
+
child.children.forEach(c => {
|
92
|
+
if (c.type === 'text') {
|
93
|
+
text += c.value;
|
94
|
+
}
|
95
|
+
});
|
96
|
+
}
|
97
|
+
});
|
98
|
+
return text;
|
99
|
+
};
|
8
100
|
|
9
101
|
export default (props: { moduleName: string }) => {
|
10
102
|
const lang = useLang();
|
@@ -19,6 +111,7 @@ export default (props: { moduleName: string }) => {
|
|
19
111
|
<div className="rspress-plugin-api-docgen">
|
20
112
|
<ReactMarkdown
|
21
113
|
remarkPlugins={[[remarkGfm]]}
|
114
|
+
rehypePlugins={[[rehypeHeaderAnchor]]}
|
22
115
|
components={
|
23
116
|
getCustomMDXComponent() as Record<string, React.ElementType>
|
24
117
|
}
|