crabatool 1.0.706 → 1.0.707
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/.eslintrc.json +8 -0
- package/package.json +2 -1
- package/tool/addNodeId.js +154 -0
- package/tool/checkjs.js +32 -1
package/.eslintrc.json
CHANGED
|
@@ -11,11 +11,19 @@
|
|
|
11
11
|
"ecmaVersion": "latest",
|
|
12
12
|
"sourceType": "script"
|
|
13
13
|
},
|
|
14
|
+
"plugins": [],
|
|
14
15
|
"rules": {
|
|
15
16
|
"semi": "off",
|
|
16
17
|
"no-redeclare": "off",
|
|
17
18
|
//"no-unused-vars": "off",
|
|
18
19
|
"missingSemi": "off",
|
|
20
|
+
"no-restricted-syntax": [
|
|
21
|
+
"error",
|
|
22
|
+
{
|
|
23
|
+
"selector": "ForInStatement",
|
|
24
|
+
"message": "1. 如果当前是数组,不建议使用for..in循环,会遍历出原型链的方法容易出现意外问题,可以改用forEach和普通for循环;2. 如果是对象,就可以用for..in;3.for of有兼容性不能用"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
19
27
|
"comma-dangle": [
|
|
20
28
|
"error",
|
|
21
29
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crabatool",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.707",
|
|
4
4
|
"description": "crabatool",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"eslint-plugin-native-ie": "0.1.2",
|
|
63
63
|
"express": "^4.17.3",
|
|
64
64
|
"express-session": "^1.17.1",
|
|
65
|
+
"fast-xml-parser": "^5.2.5",
|
|
65
66
|
"fs": "^0.0.1-security",
|
|
66
67
|
"htmlparser2": "^8.0.1",
|
|
67
68
|
"http-proxy-middleware": "^2.0.6",
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const { XMLParser, XMLBuilder } = require('fast-xml-parser');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
var config = require('../lib/config.js');
|
|
6
|
+
|
|
7
|
+
// const webPath = config.webPath
|
|
8
|
+
const webPath = "/Users/yj/Desktop/carpaevery/www"
|
|
9
|
+
const propKey = ':@';
|
|
10
|
+
const parseConfig = {
|
|
11
|
+
alwaysCreateTextNode: true,
|
|
12
|
+
attributeNamePrefix: '',
|
|
13
|
+
// attributesGroupName: 'properties',
|
|
14
|
+
textNodeName: '#text',
|
|
15
|
+
ignoreAttributes: false,
|
|
16
|
+
removeNSPrefix: true,
|
|
17
|
+
parseNodeValue: true,
|
|
18
|
+
parseAttributeValue: false,
|
|
19
|
+
allowBooleanAttributes: true,
|
|
20
|
+
trimValues: true,
|
|
21
|
+
cdataTagName: '#cdata',
|
|
22
|
+
preserveOrder: true,
|
|
23
|
+
numberParseOptions: {
|
|
24
|
+
hex: false,
|
|
25
|
+
leadingZeros: true,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// 黑名单目录列表
|
|
29
|
+
const blacklistDirs = [
|
|
30
|
+
'node_modules',
|
|
31
|
+
'.git',
|
|
32
|
+
'.svn',
|
|
33
|
+
'.vscode',
|
|
34
|
+
'dist',
|
|
35
|
+
'build',
|
|
36
|
+
'temp',
|
|
37
|
+
'tmp',
|
|
38
|
+
'.idea',
|
|
39
|
+
'__pycache__'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
//黑名单标签列表
|
|
43
|
+
const blacklistTags = [
|
|
44
|
+
'?xml',
|
|
45
|
+
'#text',
|
|
46
|
+
'Style'
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const parser = new XMLParser(parseConfig);
|
|
50
|
+
const builder = new XMLBuilder();
|
|
51
|
+
|
|
52
|
+
function parseGspx(filePath) {
|
|
53
|
+
const xml = fs.readFileSync(filePath, 'utf8');
|
|
54
|
+
const json = parser.parse(xml);
|
|
55
|
+
return json;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// [
|
|
59
|
+
// { '?xml': [ [Object] ], ':@': { version: '1.0', encoding: 'UTF-8' } },
|
|
60
|
+
// {
|
|
61
|
+
// Page: [ [Object], [Object], [Object] ],
|
|
62
|
+
// ':@': {
|
|
63
|
+
// Title: '订单处理',
|
|
64
|
+
// ActiveControl: 'null',
|
|
65
|
+
// CssClass: 'pd0 shglPage',
|
|
66
|
+
// ActionType: 'www.demo.ddclAction, /demo/ddcl.js'
|
|
67
|
+
// }
|
|
68
|
+
// }
|
|
69
|
+
// ]
|
|
70
|
+
|
|
71
|
+
// 1.遍历config.webPath下的所有gspx文件
|
|
72
|
+
// 2.读取.gspx文件,转换为json
|
|
73
|
+
// 3.遍历json,找到所有节点
|
|
74
|
+
// 3.1 找到所有节点后,判断节点是否包含ID或DataField属性
|
|
75
|
+
// 3.2 如果包含,不用管
|
|
76
|
+
// 3.3 如果不包含,则给节点添加ID
|
|
77
|
+
// 4.给每个节点添加id
|
|
78
|
+
// 5.保存gspx文件
|
|
79
|
+
function addNodeId(json, filter) {
|
|
80
|
+
if (!json.length) return;
|
|
81
|
+
|
|
82
|
+
for (const item of json) {
|
|
83
|
+
const tagName = getTagName(item);
|
|
84
|
+
if (blacklistTags.includes(tagName)) continue; // 如果标签名在黑名单中,则跳过
|
|
85
|
+
if (!isUpperCase(tagName[0])) continue; // 如果标签名第一个字符不是大写字母,则跳过
|
|
86
|
+
|
|
87
|
+
const childNodes = item[tagName];
|
|
88
|
+
let properties = item[propKey]
|
|
89
|
+
if (!properties) {
|
|
90
|
+
properties = item[propKey] = {};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (childNodes.length) { // 如果节点有子节点,则递归处理子节点
|
|
94
|
+
addNodeId(childNodes, filter);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!filter(properties, tagName)) { // 如果节点不包含ID或DataField属性,则给节点添加ID
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getTagName(json) {
|
|
103
|
+
return Object.keys(json).find(key => Array.isArray(json[key]));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isUpperCase(char) {
|
|
107
|
+
if (char >= 'A' && char <= 'Z') {
|
|
108
|
+
return true;
|
|
109
|
+
} else {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function saveGspx(filePath, json) {
|
|
115
|
+
const xml = builder.build(json);
|
|
116
|
+
fs.writeFileSync(filePath, xml);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const filter = (item, tagName) => (tagName === 'Page' || item.ID || item.DataField);
|
|
120
|
+
|
|
121
|
+
function readGspxFile(filePath, basePath = webPath) {
|
|
122
|
+
const files = fs.readdirSync(filePath);
|
|
123
|
+
files.forEach(file => {
|
|
124
|
+
const fullPath = path.join(filePath, file);
|
|
125
|
+
const relativePath = path.relative(basePath, fullPath);
|
|
126
|
+
|
|
127
|
+
if (fs.lstatSync(fullPath).isDirectory()) {
|
|
128
|
+
// 检查是否在黑名单中
|
|
129
|
+
if (blacklistDirs.includes(file)) {
|
|
130
|
+
console.log(`跳过黑名单目录: ${relativePath}/`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// 如果是目录,递归处理
|
|
134
|
+
// console.log(`目录: ${relativePath}/`);
|
|
135
|
+
readGspxFile(fullPath, basePath);
|
|
136
|
+
} else if (file.endsWith('.gspx')) {
|
|
137
|
+
// 如果是gspx文件,处理文件
|
|
138
|
+
// console.log(`处理文件: ${relativePath}`);
|
|
139
|
+
const json = parseGspx(fullPath);
|
|
140
|
+
addNodeId(json, filter);
|
|
141
|
+
// saveGspx(fullPath, json);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function start() {
|
|
147
|
+
readGspxFile(webPath);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
start();
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
package/tool/checkjs.js
CHANGED
|
@@ -18,7 +18,7 @@ var fileSize = 300;
|
|
|
18
18
|
var blackList = ['agency.js', 'jquery.js', 'craba.min.js', 'crabaEx.min.js', 'crabaNgp.js', 'echarts-all.js', 'echarts.min.js', 'math.min.js', 'craba.rollup.min.js'];
|
|
19
19
|
|
|
20
20
|
// 语法规则,不能包含这些关键词
|
|
21
|
-
var dangerousJs = ['
|
|
21
|
+
var dangerousJs = ['\=\>\\s?\\(?\\{', '\\sawait\\s', '\\.then\\(', '\\sdebugger\\s'];// 'for\\s*\\(\\s*var\\s*\\w+\\s*in'];
|
|
22
22
|
|
|
23
23
|
// gspx无用标签的修改建议
|
|
24
24
|
var warnTagsInfo = {
|
|
@@ -58,6 +58,7 @@ module.exports.start = async function() {
|
|
|
58
58
|
var errDatas = {};
|
|
59
59
|
var undefDatas = {};
|
|
60
60
|
var trimErrDatas = {};
|
|
61
|
+
var forinDatas = {};
|
|
61
62
|
var warnDatas = {};
|
|
62
63
|
var infoDatas = {};
|
|
63
64
|
var bigList = [];
|
|
@@ -115,6 +116,7 @@ module.exports.start = async function() {
|
|
|
115
116
|
var infoList = [];
|
|
116
117
|
var ieList = [];
|
|
117
118
|
var undefList = [];
|
|
119
|
+
var forinList = [];
|
|
118
120
|
var res = eslintResults[0];
|
|
119
121
|
res.messages.forEach((item) => {
|
|
120
122
|
var reg = new RegExp(/'\$\w+'\sis\snot\sdefined\./);
|
|
@@ -133,6 +135,8 @@ module.exports.start = async function() {
|
|
|
133
135
|
} else if (item.ruleId.includes('comma-dangle')) {
|
|
134
136
|
list = ieList;
|
|
135
137
|
item.message += '多余的逗号';
|
|
138
|
+
} else if (item.ruleId.includes('no-restricted-syntax')) {
|
|
139
|
+
list = forinList;
|
|
136
140
|
} else if (item.ruleId.indexOf('no-unused-vars') > -1) {
|
|
137
141
|
if (item.message && (item.message.includes("'sender'") || item.message.includes("'eventArgs'")) || item.message.includes("'args'")) {
|
|
138
142
|
return;
|
|
@@ -150,6 +154,9 @@ module.exports.start = async function() {
|
|
|
150
154
|
if (ieList.length > 0) {
|
|
151
155
|
errDatas[filePath] = ieList;
|
|
152
156
|
}
|
|
157
|
+
if (forinList.length > 0) {
|
|
158
|
+
forinDatas[filePath] = forinList;
|
|
159
|
+
}
|
|
153
160
|
if (infoList.length > 0) {
|
|
154
161
|
infoDatas[filePath] = infoList;
|
|
155
162
|
}
|
|
@@ -194,6 +201,7 @@ module.exports.start = async function() {
|
|
|
194
201
|
var trimErrKeys = Object.keys(trimErrDatas);
|
|
195
202
|
var errKeys = Object.keys(errDatas);
|
|
196
203
|
var undefKeys = Object.keys(undefDatas);
|
|
204
|
+
var forinKeys = Object.keys(forinDatas);
|
|
197
205
|
var warnKeys = Object.keys(warnDatas);
|
|
198
206
|
var infoKeys = Object.keys(infoDatas);
|
|
199
207
|
var reportData = {
|
|
@@ -236,6 +244,7 @@ module.exports.start = async function() {
|
|
|
236
244
|
info.push("| ------ | ------ | ----- |");
|
|
237
245
|
info.push(`| js语法兼容性 | ${errKeys.length}个 |${calc(errKeys.length, jsFiles.length)}|`);
|
|
238
246
|
info.push(`| js语法undefined | ${undefKeys.length}个 |${calc(undefKeys.length, jsFiles.length)}|`);
|
|
247
|
+
info.push(`| for..in语法 | ${forinKeys.length}个 |${calc(forinKeys.length, jsFiles.length)}|`);
|
|
239
248
|
info.push(`| trim、trimEnd或trimStart异常数 | ${trimErrKeys.length}个 |${calc(trimErrKeys.length, jsFiles.length)}|`);
|
|
240
249
|
info.push(`| 语法警告文件数Warn | ${warnKeys.length}个 |${calc(warnKeys.length, jsFiles.length)}|`);
|
|
241
250
|
info.push(`| 语法提示文件数Info | ${infoKeys.length}个 |${calc(infoKeys.length, jsFiles.length)}|`);
|
|
@@ -256,6 +265,7 @@ module.exports.start = async function() {
|
|
|
256
265
|
reportData.data = {
|
|
257
266
|
errorCount: errKeys.length,
|
|
258
267
|
undefCount: undefKeys.length, // 未定义总数
|
|
268
|
+
forinCount: forinKeys.length,
|
|
259
269
|
warnCount: warnKeys.length,
|
|
260
270
|
tipCount: infoKeys.length,
|
|
261
271
|
fileSize: fileSize,
|
|
@@ -284,6 +294,7 @@ module.exports.start = async function() {
|
|
|
284
294
|
//if (errKeys.length > 0) {
|
|
285
295
|
webhookList.push(`1. js语法兼容性:${errKeys.length}个,占比${calc(errKeys.length, jsFiles.length)}`);
|
|
286
296
|
webhookList.push(`1. js语法undefined:${undefKeys.length}个,占比${calc(undefKeys.length, jsFiles.length)}`);
|
|
297
|
+
webhookList.push(`1. for..in语法:${forinKeys.length}个,占比${calc(forinKeys.length, jsFiles.length)}`);
|
|
287
298
|
|
|
288
299
|
// 随机显示5个异常文件到钉钉消息
|
|
289
300
|
var errCount = reportData.data.errCount + reportData.data.undefCount;
|
|
@@ -368,6 +379,26 @@ module.exports.start = async function() {
|
|
|
368
379
|
});
|
|
369
380
|
}
|
|
370
381
|
|
|
382
|
+
if (forinKeys.length > 0) {
|
|
383
|
+
detail.push("");
|
|
384
|
+
detail.push(`# ${forinKeys.length}个for in(建议修复)`);
|
|
385
|
+
detail.push(`::: tip`);
|
|
386
|
+
detail.push(`1. 如果当前是数组,不建议使用for..in循环,会遍历出原型链的方法容易出现意外问题,可以改用forEach和普通for循环;2. 如果是对象,就可以用for..in;3.for of有兼容性不能用`);
|
|
387
|
+
detail.push(`:::`);
|
|
388
|
+
forinKeys.forEach((fileName, index) => {
|
|
389
|
+
|
|
390
|
+
detail.push(`${index + 1}. ${fileName}`);
|
|
391
|
+
|
|
392
|
+
var infoList = forinDatas[fileName];
|
|
393
|
+
detail.push("| 类型 | 行号 | 列号 | 说明 |");
|
|
394
|
+
detail.push("| ----- | ------ | ---- | ---- |");
|
|
395
|
+
infoList.forEach((item) => {
|
|
396
|
+
detail.push(`| ${item.char.trim()} | ${item.line || '-'} | ${item.column || '-'} | ${item.msg || '-'} |`);
|
|
397
|
+
sonar.issues.push(getSonarEntity('未定义', modName, item.char.trim(), item.msg, fileName, item.line, item.column, item.endLine));
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
371
402
|
|
|
372
403
|
if (trimErrKeys.length > 0) {
|
|
373
404
|
detail.push("");
|