@rspress/plugin-api-docgen 2.0.0 → 2.0.1
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/dist/index.js +70 -54
- package/package.json +4 -4
- package/static/global-components/API.tsx +2 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,51 @@
|
|
|
1
1
|
import node_fs from "node:fs";
|
|
2
|
-
import node_path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import chokidar from "chokidar";
|
|
2
|
+
import node_path, { extname, join } from "node:path";
|
|
3
|
+
import { logger } from "@rspress/core";
|
|
5
4
|
import { withCompilerOptions, withCustomConfig, withDefaultConfig } from "react-docgen-typescript";
|
|
5
|
+
const PLUGIN_VIRTUAL_MODULE_NAME = 'rsbuild:virtual-module';
|
|
6
|
+
const pluginVirtualModule = (pluginOptions = {})=>({
|
|
7
|
+
name: PLUGIN_VIRTUAL_MODULE_NAME,
|
|
8
|
+
async setup (api) {
|
|
9
|
+
const { virtualModules = {}, tempDir: virtualFolderName = '.rsbuild-virtual-module' } = pluginOptions;
|
|
10
|
+
const TEMP_DIR = join(api.context.rootPath, virtualFolderName);
|
|
11
|
+
const virtualFileAbsolutePaths = Object.keys(virtualModules).map((i)=>{
|
|
12
|
+
let absolutePath = join(TEMP_DIR, i);
|
|
13
|
+
if (!extname(absolutePath)) absolutePath = `${absolutePath}.js`;
|
|
14
|
+
return [
|
|
15
|
+
i,
|
|
16
|
+
absolutePath
|
|
17
|
+
];
|
|
18
|
+
});
|
|
19
|
+
api.modifyBundlerChain((chain, { rspack })=>{
|
|
20
|
+
const pluginId = 'RSBUILD_VIRTUAL_MODULE_PLUGIN';
|
|
21
|
+
const virtualModules = Object.fromEntries(virtualFileAbsolutePaths.map((i)=>[
|
|
22
|
+
i[1],
|
|
23
|
+
''
|
|
24
|
+
]));
|
|
25
|
+
if (chain.plugins.has(pluginId)) chain.plugin(pluginId).tap((options)=>{
|
|
26
|
+
const existingPaths = new Set(Object.keys(options[0] ?? {}));
|
|
27
|
+
const conflictPath = virtualFileAbsolutePaths.find(([, path])=>existingPaths.has(path));
|
|
28
|
+
if (conflictPath) throw new Error(`Virtual module name conflicted: ${conflictPath[0]}`);
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
...options[0],
|
|
32
|
+
...virtualModules
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
});
|
|
36
|
+
else chain.plugin(pluginId).use(rspack.experiments.VirtualModulesPlugin, [
|
|
37
|
+
virtualModules
|
|
38
|
+
]);
|
|
39
|
+
chain.resolve.alias.merge(Object.fromEntries(virtualFileAbsolutePaths));
|
|
40
|
+
});
|
|
41
|
+
for (const [moduleName, absolutePath] of virtualFileAbsolutePaths){
|
|
42
|
+
const handler = virtualModules[moduleName];
|
|
43
|
+
if (handler) api.transform({
|
|
44
|
+
test: absolutePath
|
|
45
|
+
}, handler);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
6
49
|
node_path.join(__dirname, '..');
|
|
7
50
|
const apiDocMap = {};
|
|
8
51
|
const locales = {
|
|
@@ -56,16 +99,13 @@ const locales = {
|
|
|
56
99
|
}
|
|
57
100
|
};
|
|
58
101
|
const isToolEntries = (obj)=>!!obj.documentation || !!obj["react-docgen-typescript"];
|
|
59
|
-
const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptions
|
|
60
|
-
const
|
|
102
|
+
const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptions })=>{
|
|
103
|
+
const sourceFilePaths = [];
|
|
61
104
|
const genApiDoc = async (entry, tool)=>{
|
|
62
105
|
if (0 === Object.keys(entry).length) return;
|
|
63
106
|
await Promise.all(Object.entries(entry).map(async ([key, value])=>{
|
|
64
107
|
const moduleSourceFilePath = node_path.resolve(appDir, value);
|
|
65
|
-
|
|
66
|
-
apiParseTool,
|
|
67
|
-
moduleName: key
|
|
68
|
-
};
|
|
108
|
+
sourceFilePaths.push(moduleSourceFilePath);
|
|
69
109
|
try {
|
|
70
110
|
if ('documentation' === tool) {
|
|
71
111
|
const documentation = await import("documentation");
|
|
@@ -113,42 +153,8 @@ const docgen = async ({ entries, languages, apiParseTool, appDir, parseToolOptio
|
|
|
113
153
|
genApiDoc(documentationEntries, 'documentation')
|
|
114
154
|
]);
|
|
115
155
|
} else await genApiDoc(entries, apiParseTool);
|
|
116
|
-
if (!isProd) {
|
|
117
|
-
const watcher = chokidar.watch(Object.keys(watchFileMap), {
|
|
118
|
-
ignoreInitial: true,
|
|
119
|
-
ignorePermissionErrors: true,
|
|
120
|
-
ignored: [
|
|
121
|
-
/node_modules/
|
|
122
|
-
]
|
|
123
|
-
});
|
|
124
|
-
let isUpdate = false;
|
|
125
|
-
watcher.on('change', (changed)=>{
|
|
126
|
-
if (isUpdate) return;
|
|
127
|
-
isUpdate = true;
|
|
128
|
-
logger.info('[module-doc-plugin]', 'updating API');
|
|
129
|
-
const watchFileInfo = watchFileMap[changed];
|
|
130
|
-
if (watchFileInfo) {
|
|
131
|
-
const { apiParseTool, moduleName } = watchFileInfo;
|
|
132
|
-
const updateSiteData = ()=>{
|
|
133
|
-
const siteDataPath = node_path.join(process.cwd(), 'node_modules', RSPRESS_TEMP_DIR, 'runtime', 'virtual-site-data.mjs');
|
|
134
|
-
import(siteDataPath).then((siteData)=>{
|
|
135
|
-
const data = {
|
|
136
|
-
...siteData.default
|
|
137
|
-
};
|
|
138
|
-
data.pages.forEach((page)=>{
|
|
139
|
-
page.apiDocMap = apiDocMap;
|
|
140
|
-
});
|
|
141
|
-
node_fs.writeFileSync(siteDataPath, `export default ${JSON.stringify(data)}`);
|
|
142
|
-
isUpdate = false;
|
|
143
|
-
});
|
|
144
|
-
};
|
|
145
|
-
genApiDoc({
|
|
146
|
-
[moduleName]: changed
|
|
147
|
-
}, apiParseTool).then(updateSiteData);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
156
|
logger.success('[module-doc-plugin]', 'Generate API table successfully!');
|
|
157
|
+
return sourceFilePaths;
|
|
152
158
|
};
|
|
153
159
|
function generateTable(componentDoc, language) {
|
|
154
160
|
return componentDoc.map((param)=>{
|
|
@@ -197,6 +203,7 @@ function generateTable(componentDoc, language) {
|
|
|
197
203
|
`;
|
|
198
204
|
}).join('\n');
|
|
199
205
|
}
|
|
206
|
+
const VIRTUAL_MODULE_NAME = 'rspress-plugin-api-docgen-map';
|
|
200
207
|
function pluginApiDocgen(options) {
|
|
201
208
|
const { entries = {}, apiParseTool = "react-docgen-typescript", appDir = process.cwd(), parseToolOptions = {} } = options || {};
|
|
202
209
|
return {
|
|
@@ -205,7 +212,7 @@ function pluginApiDocgen(options) {
|
|
|
205
212
|
config.markdown = config.markdown || {};
|
|
206
213
|
return config;
|
|
207
214
|
},
|
|
208
|
-
async beforeBuild (config
|
|
215
|
+
async beforeBuild (config) {
|
|
209
216
|
let languages = (config.themeConfig?.locales?.map((locale)=>locale.lang) || config.locales?.map((locale)=>locale.lang) || []).filter((lang)=>[
|
|
210
217
|
'zh',
|
|
211
218
|
'en',
|
|
@@ -215,20 +222,29 @@ function pluginApiDocgen(options) {
|
|
|
215
222
|
if (0 === languages.length) languages = [
|
|
216
223
|
defaultLang
|
|
217
224
|
];
|
|
218
|
-
|
|
225
|
+
const docgenOptions = {
|
|
219
226
|
entries,
|
|
220
227
|
apiParseTool,
|
|
221
228
|
languages,
|
|
222
229
|
appDir,
|
|
223
|
-
parseToolOptions
|
|
224
|
-
isProd
|
|
225
|
-
});
|
|
226
|
-
config.builderConfig = config.builderConfig || {};
|
|
227
|
-
config.builderConfig.source = config.builderConfig.source || {};
|
|
228
|
-
config.builderConfig.source.define = {
|
|
229
|
-
...config.builderConfig.source.define,
|
|
230
|
-
RSPRESS_PLUGIN_API_DOCGEN_MAP: JSON.stringify(apiDocMap)
|
|
230
|
+
parseToolOptions
|
|
231
231
|
};
|
|
232
|
+
const sourceFiles = await docgen(docgenOptions);
|
|
233
|
+
let isFirstTransform = true;
|
|
234
|
+
config.builderConfig = config.builderConfig || {};
|
|
235
|
+
config.builderConfig.plugins = [
|
|
236
|
+
...config.builderConfig.plugins || [],
|
|
237
|
+
pluginVirtualModule({
|
|
238
|
+
virtualModules: {
|
|
239
|
+
[VIRTUAL_MODULE_NAME]: async ({ addDependency })=>{
|
|
240
|
+
if (isFirstTransform) isFirstTransform = false;
|
|
241
|
+
else await docgen(docgenOptions);
|
|
242
|
+
for (const filePath of sourceFiles)addDependency(filePath);
|
|
243
|
+
return `export default ${JSON.stringify(apiDocMap)};`;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
];
|
|
232
248
|
},
|
|
233
249
|
async modifySearchIndexData (pages) {
|
|
234
250
|
const apiCompRegExp = /(<API\s+moduleName=['"](\S+)['"]\s*(.*)?\/>)|(<API\s+moduleName=['"](\S+)['"]\s*(.*)?>(.*)?<\/API>)/;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rspress/plugin-api-docgen",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
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": {
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"static"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"chokidar": "^3.6.0",
|
|
26
25
|
"documentation": "14.0.3",
|
|
27
26
|
"github-slugger": "^2.0.0",
|
|
28
27
|
"react-docgen-typescript": "2.4.0",
|
|
@@ -43,10 +42,11 @@
|
|
|
43
42
|
"react-dom": "^19.2.4",
|
|
44
43
|
"react-router-dom": "^7.12.0",
|
|
45
44
|
"rsbuild-plugin-publint": "^0.3.4",
|
|
45
|
+
"rsbuild-plugin-virtual-module": "0.4.1",
|
|
46
46
|
"typescript": "^5.8.2"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@rspress/core": "^2.0.
|
|
49
|
+
"@rspress/core": "^2.0.1",
|
|
50
50
|
"typescript": "^5.8.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
|
-
"node": "
|
|
58
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public",
|
|
@@ -8,13 +8,11 @@ import remarkGfm from 'remark-gfm';
|
|
|
8
8
|
import './API.css';
|
|
9
9
|
// biome-ignore lint/style/useImportType: <exact>
|
|
10
10
|
import React from 'react';
|
|
11
|
+
// @ts-expect-error virtual module
|
|
12
|
+
import apiDocMap from 'rspress-plugin-api-docgen-map';
|
|
11
13
|
import type { Plugin } from 'unified';
|
|
12
14
|
import { visit } from 'unist-util-visit';
|
|
13
15
|
|
|
14
|
-
declare global {
|
|
15
|
-
var RSPRESS_PLUGIN_API_DOCGEN_MAP: Record<string, string>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
16
|
function headingRank(node: Root | Content): number | null {
|
|
19
17
|
const name =
|
|
20
18
|
(node && node.type === 'element' && node.tagName.toLowerCase()) || '';
|
|
@@ -103,9 +101,6 @@ const collectHeaderText = (node: Element): string => {
|
|
|
103
101
|
const API = (props: { moduleName: string }) => {
|
|
104
102
|
const lang = useLang();
|
|
105
103
|
const { moduleName } = props;
|
|
106
|
-
// some api doc have two languages.
|
|
107
|
-
const apiDocMap = RSPRESS_PLUGIN_API_DOCGEN_MAP;
|
|
108
|
-
// avoid error when no page data
|
|
109
104
|
const apiDoc = apiDocMap?.[`${moduleName}-${lang}`] || '';
|
|
110
105
|
return (
|
|
111
106
|
<div className="rspress-plugin-api-docgen">
|