@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 CHANGED
@@ -1,8 +1,51 @@
1
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";
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, isProd })=>{
60
- const watchFileMap = {};
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
- watchFileMap[moduleSourceFilePath] = {
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, isProd) {
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
- await docgen({
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.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.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": ">=20.9.0"
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">