@rspress/plugin-api-docgen 2.0.0-beta.2 → 2.0.0-beta.20

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