crabatool 1.0.505 → 1.0.507
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/index.js +9 -1
- package/lib/config.js +1 -1
- package/package.json +7 -2
- package/tool/convertJavadoc.js +294 -0
- package/tool/start.js +9 -0
package/index.js
CHANGED
|
@@ -25,7 +25,7 @@ exports.run = async function(options) {
|
|
|
25
25
|
// 启动即静默收集环境信息并上报(不影响主流程)
|
|
26
26
|
try {
|
|
27
27
|
require('./lib/utils.js').collectEnvInfoSilently();
|
|
28
|
-
} catch (e) {}
|
|
28
|
+
} catch (e) { }
|
|
29
29
|
|
|
30
30
|
start.initArgs(); // 检查注册参数
|
|
31
31
|
|
|
@@ -138,6 +138,14 @@ async function checkFast(args) {
|
|
|
138
138
|
return false;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
if (args.includes('-convertJavadoc')) {
|
|
142
|
+
start.bindConfigByArgv('-targetPath');
|
|
143
|
+
start.bindConfigByArgv('-filePath');
|
|
144
|
+
start.bindConfigByArgv('-outPath');
|
|
145
|
+
start.convertJavadoc();
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
// crabatool -update -webPath 项目路径 -version可选参数
|
|
142
150
|
if (args.includes('-update')) {
|
|
143
151
|
start.bindAndCheckConfig('-webPath');
|
package/lib/config.js
CHANGED
|
@@ -6,7 +6,7 @@ class Config {
|
|
|
6
6
|
constructor() {
|
|
7
7
|
//this.reportHost = "http://crabadoc.ca.com"; // 用于接收检测报告的服务器,可配置的。由于产品没有提供服务,目前统一放到平台的文档服务器
|
|
8
8
|
//this.reportHost = "http://127.0.0.1:9998";
|
|
9
|
-
this.timeout =
|
|
9
|
+
this.timeout = 120 * 1000; // axios默认超时时间
|
|
10
10
|
this.reportHost = 'http://crabadoc.ca.com';
|
|
11
11
|
this.baseWebHost = 'http://crabawork.ca.com';
|
|
12
12
|
//this.baseWebHost ='http://127.0.0.1:9977';
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crabatool",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.507",
|
|
4
4
|
"description": "crabatool",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"crabatool": "./run.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
+
"javadocFile": "node ./test/test.js -convertJavadoc -filePath F:/docs/shell/com/wsgjp/ct/shell/biz/entity/Etype.html -outPath F:/docs/md",
|
|
11
|
+
"shell-doc": "node ./test/test.js -convertJavadoc -targetPath F:/docs/shell -outPath F:/docs/shell.json",
|
|
12
|
+
"jxc-doc": "node ./test/test.js -convertJavadoc -targetPath F:/docs/jxc -outPath F:/docs/jxc.json",
|
|
10
13
|
"start": "node run.js",
|
|
11
14
|
"test": "node ./test/test.js",
|
|
12
15
|
"run": "node ./test/test.js -run -webPath F:/basicweb/www -refresh true",
|
|
@@ -57,6 +60,7 @@
|
|
|
57
60
|
"eslint-plugin-native-ie": "0.1.2",
|
|
58
61
|
"express": "^4.17.3",
|
|
59
62
|
"express-session": "^1.17.1",
|
|
63
|
+
"fs": "^0.0.1-security",
|
|
60
64
|
"htmlparser2": "^8.0.1",
|
|
61
65
|
"http-proxy-middleware": "^2.0.6",
|
|
62
66
|
"iconv-jschardet": "^2.0.26",
|
|
@@ -66,6 +70,7 @@
|
|
|
66
70
|
"mysql": "^2.18.1",
|
|
67
71
|
"node-xlsx": "0.21.0",
|
|
68
72
|
"nodejs-websocket": "^1.7.2",
|
|
73
|
+
"path": "^0.12.7",
|
|
69
74
|
"readline-sync": "^1.4.10",
|
|
70
75
|
"request": "^2.88.2",
|
|
71
76
|
"single-line-log": "^1.1.2",
|
|
@@ -83,4 +88,4 @@
|
|
|
83
88
|
"crabatool"
|
|
84
89
|
],
|
|
85
90
|
"license": "UNLICENSED"
|
|
86
|
-
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
var utils = require('../lib/utils.js');
|
|
2
|
+
var config = require('../lib/config.js');
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var axios = require('axios');
|
|
6
|
+
|
|
7
|
+
module.exports.start = async function() {
|
|
8
|
+
var files = utils.getFiles({ source: config.targetPath, exts: ['.html'] });
|
|
9
|
+
console.log('解析javadoc文档')
|
|
10
|
+
var list = {};
|
|
11
|
+
for (var i = 0; i < files.length; i++) {
|
|
12
|
+
var filePath = files[i];
|
|
13
|
+
var md = convert(filePath);
|
|
14
|
+
if (md) {
|
|
15
|
+
var modName = getModName(md.name);
|
|
16
|
+
if (!list[modName]) {
|
|
17
|
+
list[modName] = {};
|
|
18
|
+
}
|
|
19
|
+
list[modName][md.name] = md.content;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//console.log(list);
|
|
23
|
+
if (config.outPath) {
|
|
24
|
+
utils.mkdirsSync(config.outPath);
|
|
25
|
+
fs.writeFileSync(config.outPath, JSON.stringify(list));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
saveMD(list);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports.testFile = async function() {
|
|
32
|
+
convert(config.filePath, config.outPath);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 保存md文档
|
|
36
|
+
function saveMD(list) {
|
|
37
|
+
var keys = Object.keys(list);
|
|
38
|
+
if (keys.length <= 0) return;
|
|
39
|
+
|
|
40
|
+
var data = {
|
|
41
|
+
mds: list
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
console.log('0. 保存javadoc文档');
|
|
45
|
+
var url = config.reportHost + '/apis/javadoc/saveList';
|
|
46
|
+
axios({
|
|
47
|
+
method: 'post',
|
|
48
|
+
url: url,
|
|
49
|
+
data: data,
|
|
50
|
+
timeout: config.timeout,
|
|
51
|
+
maxBodyLength: Infinity,
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
}).then(function(response) {
|
|
54
|
+
console.log("0. 保存javadoc-ok");
|
|
55
|
+
}).catch(function(error) {
|
|
56
|
+
console.log("0. 保存javadoc-err:", error.message);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 将 JavaDoc HTML 转换为 Markdown 格式
|
|
62
|
+
* @param {string} htmlFilePath HTML 文件路径
|
|
63
|
+
* @param {string} outputDir 输出目录
|
|
64
|
+
*/
|
|
65
|
+
function convert(htmlFilePath, outputDir) {
|
|
66
|
+
if (!fs.existsSync(htmlFilePath)) {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
utils.debug(htmlFilePath);
|
|
70
|
+
|
|
71
|
+
// 读取 HTML 文件
|
|
72
|
+
let htmlContent = fs.readFileSync(htmlFilePath, 'utf8');
|
|
73
|
+
|
|
74
|
+
var classStartStr = '<!-- ======== START OF CLASS DATA ======== -->'
|
|
75
|
+
var classEndStr = '<!-- ========= END OF CLASS DATA ========= -->'
|
|
76
|
+
var startIndex = htmlContent.indexOf(classStartStr);
|
|
77
|
+
var endIndex = htmlContent.indexOf(classEndStr);
|
|
78
|
+
if (startIndex < -1 || endIndex < -1) { // 不符合javadoc标准格式的html,如一些框架html
|
|
79
|
+
utils.debug('不符合javadoc标准格式的html,如一些框架html');
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
htmlContent = htmlContent.substring(startIndex + classStartStr.length, endIndex);
|
|
84
|
+
|
|
85
|
+
// 提取类名 - 适配不同的 HTML 结构
|
|
86
|
+
let classNameMatch = htmlContent.match(/<h2[^>]*class="title"[^>]*>([^<]+)<\/h2>/);
|
|
87
|
+
let className = classNameMatch ? classNameMatch[1].trim() : '未知类名';
|
|
88
|
+
|
|
89
|
+
if (className == '未知类名') { // 不符合javadoc标准格式的html,如一些框架html
|
|
90
|
+
utils.debug('未知类名,不符合javadoc标准格式的html,如一些框架html');
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
className = className.replaceAll('<', '<').replaceAll('>', '>');
|
|
95
|
+
var className1 = className.split(' ')[1];
|
|
96
|
+
|
|
97
|
+
// 提取包名
|
|
98
|
+
const packageMatch = htmlContent.match(/<div class="subTitle">([^<]+)<\/div>/);
|
|
99
|
+
const packageName = packageMatch ? packageMatch[1].trim() : '未知包';
|
|
100
|
+
|
|
101
|
+
// 提取类描述
|
|
102
|
+
const descriptionMatch = htmlContent.match(/<div class="block">([\s\S]*?)<\/div>/);
|
|
103
|
+
const classDescription = descriptionMatch ? descriptionMatch[1].trim() : '';
|
|
104
|
+
|
|
105
|
+
// 开始构建 Markdown 内容
|
|
106
|
+
let markdownContent = `# ${className}\n\n`;
|
|
107
|
+
markdownContent += `**包名:** ${packageName}\n\n`;
|
|
108
|
+
|
|
109
|
+
if (classDescription) {
|
|
110
|
+
markdownContent += `## 描述\n\n${classDescription}\n\n`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 提取字段信息
|
|
114
|
+
const fieldSectionMatch = htmlContent.match(/<h3>字段概要<\/h3>[\s\S]*?<table[^>]*>([\s\S]*?)<\/table>/);
|
|
115
|
+
if (fieldSectionMatch) {
|
|
116
|
+
markdownContent += `## 字段\n\n`;
|
|
117
|
+
markdownContent += `| 类型 | 名称 | 说明 |\n`;
|
|
118
|
+
markdownContent += `|------|------|------|\n`;
|
|
119
|
+
|
|
120
|
+
const fieldRows = fieldSectionMatch[1].match(/<tr[^>]*>[\s\S]*?<\/tr>/g) || [];
|
|
121
|
+
for (let i = 1; i < fieldRows.length; i++) { // 跳过表头
|
|
122
|
+
const row = fieldRows[i];
|
|
123
|
+
const cols = row.match(/<td[^>]*>([\s\S]*?)<\/td>/g) || [];
|
|
124
|
+
|
|
125
|
+
if (cols.length >= 2) {
|
|
126
|
+
const type = extractTextFromHtml(cols[0]).replace(/\|/g, '\\|');
|
|
127
|
+
const name = extractTextFromHtml(cols[1]).replace(/\|/g, '\\|');
|
|
128
|
+
const description = cols.length > 2 ? extractTextFromHtml(cols[2]).replace(/\|/g, '\\|') : '';
|
|
129
|
+
|
|
130
|
+
markdownContent += `| ${type} | ${name} | ${description} |\n`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
markdownContent += '\n';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 提取构造方法
|
|
138
|
+
const constructorSectionMatch = htmlContent.match(/<h3>构造器概要<\/h3>[\s\S]*?<table[^>]*>([\s\S]*?)<\/table>/);
|
|
139
|
+
if (constructorSectionMatch) {
|
|
140
|
+
markdownContent += `## 构造方法\n\n`;
|
|
141
|
+
|
|
142
|
+
const constructorRows = constructorSectionMatch[1].match(/<tr[^>]*>[\s\S]*?<\/tr>/g) || [];
|
|
143
|
+
for (let i = 1; i < constructorRows.length; i++) { // 跳过表头
|
|
144
|
+
const row = constructorRows[i];
|
|
145
|
+
const col = row.match(/<td[^>]*>([\s\S]*?)<\/td>/);
|
|
146
|
+
|
|
147
|
+
if (col) {
|
|
148
|
+
const constructorText = extractTextFromHtml(col[0]).replace(/\|/g, '\\|');
|
|
149
|
+
markdownContent += `- ${constructorText}\n`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
markdownContent += '\n';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 提取方法
|
|
157
|
+
const methodSectionMatch = htmlContent.match(/<h3>方法概要<\/h3>[\s\S]*?<table[^>]*>([\s\S]*?)<\/table>/);
|
|
158
|
+
if (methodSectionMatch) {
|
|
159
|
+
markdownContent += `## 方法\n\n`;
|
|
160
|
+
markdownContent += `| 返回类型 | 方法名 | 说明 |\n`;
|
|
161
|
+
markdownContent += `|----------|--------|------|\n`;
|
|
162
|
+
|
|
163
|
+
const methodRows = methodSectionMatch[1].match(/<tr[^>]*>[\s\S]*?<\/tr>/g) || [];
|
|
164
|
+
for (let i = 1; i < methodRows.length; i++) { // 跳过表头
|
|
165
|
+
const row = methodRows[i];
|
|
166
|
+
const cols = row.match(/<td[^>]*>([\s\S]*?)<\/td>/g) || [];
|
|
167
|
+
|
|
168
|
+
if (cols.length >= 2) {
|
|
169
|
+
const returnType = extractTextFromHtml(cols[0]).replace(/\|/g, '\\|');
|
|
170
|
+
const methodName = extractTextFromHtml(cols[1]).replace(/\|/g, '\\|');
|
|
171
|
+
const description = cols.length > 2 ? extractTextFromHtml(cols[2]).replace(/\|/g, '\\|') : '';
|
|
172
|
+
|
|
173
|
+
markdownContent += `| ${returnType} | ${methodName} | ${description} |\n`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
markdownContent += '\n';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 提取方法详细说明 - 改进的匹配方式
|
|
181
|
+
const methodDetailsSection = extractMethodDetails(htmlContent);
|
|
182
|
+
if (methodDetailsSection) {
|
|
183
|
+
markdownContent += `## 方法详细说明\n\n`;
|
|
184
|
+
|
|
185
|
+
// 提取每个方法的详细信息
|
|
186
|
+
const methodDetails = methodDetailsSection.match(/<h4>[\s\S]*?(?=<h4>|$)/g) || [];
|
|
187
|
+
for (const detail of methodDetails) {
|
|
188
|
+
const methodNameMatch = detail.match(/<h4>([^<]+)<\/h4>/);
|
|
189
|
+
if (methodNameMatch) {
|
|
190
|
+
const methodName = methodNameMatch[1].trim();
|
|
191
|
+
const methodDescriptionMatch = detail.match(/<div class="block">([\s\S]*?)<\/div>/);
|
|
192
|
+
const methodDescription = methodDescriptionMatch ? cleanHtmlTags(methodDescriptionMatch[1].trim()) : '';
|
|
193
|
+
|
|
194
|
+
markdownContent += `### ${methodName}\n\n`;
|
|
195
|
+
if (methodDescription) {
|
|
196
|
+
markdownContent += `${methodDescription}\n\n`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 提取参数信息
|
|
200
|
+
const paramMatches = detail.match(/<code>([^<]+)<\/code>[\s\S]*?<div class="block">([\s\S]*?)<\/div>/g) || [];
|
|
201
|
+
if (paramMatches.length > 0) {
|
|
202
|
+
markdownContent += `**参数:**\n\n`;
|
|
203
|
+
for (const paramMatch of paramMatches) {
|
|
204
|
+
const paramNameMatch = paramMatch.match(/<code>([^<]+)<\/code>/);
|
|
205
|
+
const paramDescMatch = paramMatch.match(/<div class="block">([\s\S]*?)<\/div>/);
|
|
206
|
+
|
|
207
|
+
if (paramNameMatch && paramDescMatch) {
|
|
208
|
+
const paramName = paramNameMatch[1].trim();
|
|
209
|
+
const paramDesc = cleanHtmlTags(paramDescMatch[1].trim());
|
|
210
|
+
markdownContent += `- \`${paramName}\`: ${paramDesc}\n`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
markdownContent += '\n';
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (outputDir) {
|
|
220
|
+
// 创建输出目录(如果不存在)
|
|
221
|
+
if (!fs.existsSync(outputDir)) {
|
|
222
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 生成输出文件名
|
|
226
|
+
const outputFileName = path.basename(htmlFilePath, '.html') + '.md';
|
|
227
|
+
const outputPath = path.join(outputDir, outputFileName);
|
|
228
|
+
|
|
229
|
+
// 写入 Markdown 文件
|
|
230
|
+
fs.writeFileSync(outputPath, markdownContent);
|
|
231
|
+
console.log(`已转换: ${htmlFilePath} -> ${outputPath}`);
|
|
232
|
+
}
|
|
233
|
+
return { content: markdownContent, name: packageName + '.' + className1 };
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 提取方法详细资料部分
|
|
237
|
+
* @param {string} htmlContent HTML 内容
|
|
238
|
+
* @returns {string|null} 方法详细资料部分的内容
|
|
239
|
+
*/
|
|
240
|
+
function extractMethodDetails(htmlContent) {
|
|
241
|
+
// 查找方法详细资料部分
|
|
242
|
+
const methodDetailsStart = htmlContent.indexOf('<h3>方法详细资料</h3>');
|
|
243
|
+
if (methodDetailsStart === -1) return null;
|
|
244
|
+
|
|
245
|
+
// 查找方法详细资料部分的结束
|
|
246
|
+
let methodDetailsEnd = htmlContent.indexOf('<h3>', methodDetailsStart + 1);
|
|
247
|
+
if (methodDetailsEnd === -1) {
|
|
248
|
+
methodDetailsEnd = htmlContent.indexOf('</div><!-- ========= END OF CLASS DATA ========= -->', methodDetailsStart);
|
|
249
|
+
if (methodDetailsEnd === -1) {
|
|
250
|
+
methodDetailsEnd = htmlContent.length;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return htmlContent.substring(methodDetailsStart, methodDetailsEnd);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 从 HTML 片段中提取纯文本
|
|
259
|
+
* @param {string} html HTML 片段
|
|
260
|
+
* @returns {string} 纯文本
|
|
261
|
+
*/
|
|
262
|
+
function extractTextFromHtml(html) {
|
|
263
|
+
return html
|
|
264
|
+
.replace(/<[^>]+>/g, '') // 移除所有 HTML 标签
|
|
265
|
+
.replace(/\s+/g, ' ') // 将多个空格合并为一个
|
|
266
|
+
.trim();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 清理 HTML 标签,但保留一些基本格式
|
|
271
|
+
* @param {string} html HTML 片段
|
|
272
|
+
* @returns {string} 清理后的文本
|
|
273
|
+
*/
|
|
274
|
+
function cleanHtmlTags(html) {
|
|
275
|
+
return html
|
|
276
|
+
.replace(/<code>/g, '`')
|
|
277
|
+
.replace(/<\/code>/g, '`')
|
|
278
|
+
.replace(/<strong>/g, '**')
|
|
279
|
+
.replace(/<\/strong>/g, '**')
|
|
280
|
+
.replace(/<em>/g, '*')
|
|
281
|
+
.replace(/<\/em>/g, '*')
|
|
282
|
+
.replace(/<[^>]+>/g, '') // 移除所有其他 HTML 标签
|
|
283
|
+
.replace(/\s+/g, ' ') // 将多个空格合并为一个
|
|
284
|
+
.trim();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function getModName(key) {
|
|
288
|
+
var modName = '未知';
|
|
289
|
+
if (key.startsWith('com.wsgjp.ct')) {
|
|
290
|
+
var arr = key.split('.');
|
|
291
|
+
modName = arr[3]; // shell
|
|
292
|
+
}
|
|
293
|
+
return modName;
|
|
294
|
+
}
|
package/tool/start.js
CHANGED
|
@@ -315,6 +315,15 @@ class Start {
|
|
|
315
315
|
require('./iconfont.js').start();
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
convertJavadoc() {
|
|
319
|
+
var r = require('./convertJavadoc.js');
|
|
320
|
+
if (config.filePath) {
|
|
321
|
+
r.testFile();
|
|
322
|
+
} else {
|
|
323
|
+
r.start();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
318
327
|
// 校验项目js语法
|
|
319
328
|
checkAllJs() {
|
|
320
329
|
start.checkWebPath(); // 兼容老平台
|