crabatool 1.0.803 → 1.0.808

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 CHANGED
@@ -164,6 +164,8 @@ async function checkFast(args) {
164
164
  // crabatool -autoId -webPath 项目路径
165
165
  if (args.includes('-autoId')) {
166
166
  start.bindAndCheckConfig('-webPath');
167
+ start.bindConfigByArgv('-ignoreAutoId', 'array');
168
+ start.bindConfigByArgv('-ignoreFiles', 'array');
167
169
  require('./tool/addNodeId.js').start();
168
170
  return false;
169
171
  }
@@ -287,4 +289,4 @@ async function checkFast(args) {
287
289
 
288
290
 
289
291
  return true;
290
- }
292
+ }
package/lib/config.js CHANGED
@@ -18,10 +18,13 @@ class Config {
18
18
 
19
19
  this.webhooks = "https://oapi.dingtalk.com/robot/send?access_token=ce27b1b1540881540d44c0bd05ba738d865363758892ede137dc1020bd36bd5a";
20
20
  this.crabaHooks = "https://oapi.dingtalk.com/robot/send?access_token=ce27b1b1540881540d44c0bd05ba738d865363758892ede137dc1020bd36bd5a";
21
+ this.crabaErrorHooks = 'https://oapi.dingtalk.com/robot/send?access_token=35ac432f7f0741e2a44624f82cef011eaf012f7764b1e977cb6119e4e133b961';
21
22
  //this.webhooks = "https://oapi.dingtalk.com/robot/send?access_token=37279df60e03ebf25e8eb71230ddb93fe74de99951a8d635d0458e60bfcd44d8";
22
23
  this.checkgspx = true; // 检查gspx
23
24
  this.hidejspath = false;
24
25
  this.ignoreCheck = [];
26
+ this.ignoreFiles = [];
27
+ this.ignoreAutoId = [];
25
28
  this.Version = pg.version;
26
29
  this.Debug = false;
27
30
  this.version = 'master';
@@ -46,4 +49,4 @@ class Config {
46
49
  }
47
50
  }
48
51
 
49
- module.exports = new Config();
52
+ module.exports = new Config();
package/lib/utils.js CHANGED
@@ -522,7 +522,7 @@ class Utils {
522
522
  next();
523
523
  }
524
524
 
525
- postWeebhooks(msg) {
525
+ postWeebhooks(msg, isError) {
526
526
  var data = {
527
527
  msgtype: 'markdown',
528
528
  markdown: {
@@ -533,7 +533,7 @@ class Utils {
533
533
 
534
534
  axios({
535
535
  method: 'post',
536
- url: config.crabaHooks,
536
+ url: isError ? config.crabaErrorHooks : config.crabaHooks,
537
537
  timeout: config.timeout,
538
538
  data: data,
539
539
  headers: { 'Content-Type': 'application/json' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crabatool",
3
- "version": "1.0.803",
3
+ "version": "1.0.808",
4
4
  "description": "crabatool",
5
5
  "main": "index.js",
6
6
  "bin": {
package/tool/addNodeId.js CHANGED
@@ -14,6 +14,9 @@ const config = require('../lib/config.js');
14
14
  const webPath = config.webPath;
15
15
  // const webPath = "/Users/yj/Desktop/carpaevery/www/View";
16
16
 
17
+ let normalizedIgnoreEntries = [];
18
+ let shouldIgnoreRelativePath = () => false;
19
+
17
20
  const blacklistDirs = [
18
21
  'node_modules',
19
22
  '.git',
@@ -24,16 +27,167 @@ const blacklistDirs = [
24
27
  'temp',
25
28
  'tmp',
26
29
  '.idea',
27
- '__pycache__'
30
+ '__pycache__',
31
+ '_Sys'
28
32
  ];
29
33
 
30
- const blacklistTags = [
34
+ const notParseTags = [
31
35
  '?xml',
32
36
  '#text',
33
37
  'Style',
34
- 'Script'
38
+ 'Script',
39
+ 'Meta'
40
+ ]
41
+
42
+ const blacklistTags = [
43
+ 'ListTemplate',
44
+ 'HiddenField',
45
+ 'InnerBlock',
46
+ 'ListBox',
47
+ 'Pager',
48
+ 'HintBlock',
49
+ 'VerificationCode',
50
+ 'FileUpload',
51
+ 'ToolBar',
52
+ 'ToolButton',
53
+ 'ToolImage',
54
+ 'BarScale',
55
+ 'EScanAction',
56
+ 'PosPrint',
57
+ 'BarcodePrint',
58
+ 'ExpressPrint',
59
+ 'ReportAction',
60
+ 'DataBlock',
61
+ 'HPanel',
62
+ 'VPanel',
63
+ 'NavGroup',
64
+ 'NavBar',
65
+ 'PopupBlock',
66
+ 'PopupMenu',
67
+ 'TabContainer',
68
+ 'Block',
69
+ 'VBlock',
70
+ 'HBlock',
71
+ 'FlexColumn',
72
+ 'FlexBlock',
73
+ 'FlowPanel',
74
+ 'Image',
75
+ 'PrintButton',
76
+ 'DataIndex',
77
+ 'DataCheckBox',
78
+ 'DataImage',
79
+ 'DataNumber',
80
+ 'DataNumberEdit',
81
+ 'DataText',
82
+ 'DataTextEdit',
83
+ 'HSpacer',
84
+ 'VSpacer',
85
+ 'DetailFields',
86
+ 'MasterFields',
87
+ 'Field',
88
+ 'ServerStatus',
89
+ 'StatusBar',
90
+ 'ListItem'
35
91
  ];
36
92
 
93
+ // 需要保留其内部结构,不为内部组件生成自动 ID 的标签
94
+ const skipDescendantTags = new Set([
95
+ 'CustomControl',
96
+ ]);
97
+
98
+ // 忽略规则相关工具函数
99
+ function normalizePathFragment(value) {
100
+ if (typeof value !== 'string') {
101
+ return '';
102
+ }
103
+
104
+ let normalized = value.trim();
105
+ if (!normalized) {
106
+ return '';
107
+ }
108
+
109
+ normalized = path.normalize(normalized).replace(/\\/g, '/');
110
+ normalized = normalized.replace(/^(\.\/)+/, '');
111
+ normalized = normalized.replace(/^\/+/, '');
112
+ normalized = normalized.replace(/\/+$/, '');
113
+
114
+ if (!normalized || normalized === '.') {
115
+ return '';
116
+ }
117
+
118
+ return normalized.toLowerCase();
119
+ }
120
+
121
+ function collectIgnoreEntries(entries, basePath) {
122
+ if (!Array.isArray(entries) || entries.length === 0) {
123
+ return [];
124
+ }
125
+
126
+ const resolvedBase = typeof basePath === 'string' ? path.resolve(basePath) : '';
127
+ const baseRoot = resolvedBase ? path.parse(resolvedBase).root : '';
128
+ const payload = new Set();
129
+
130
+ entries.forEach(item => {
131
+ if (typeof item !== 'string') {
132
+ return;
133
+ }
134
+
135
+ let candidate = item.trim();
136
+ if (!candidate) {
137
+ return;
138
+ }
139
+
140
+ if (path.isAbsolute(candidate)) {
141
+ if (!resolvedBase) {
142
+ return;
143
+ }
144
+
145
+ const resolvedCandidate = path.resolve(candidate);
146
+ const candidateRoot = path.parse(resolvedCandidate).root;
147
+ if (baseRoot && candidateRoot && baseRoot.toLowerCase() !== candidateRoot.toLowerCase()) {
148
+ return;
149
+ }
150
+
151
+ candidate = path.relative(resolvedBase, resolvedCandidate);
152
+ }
153
+
154
+ const normalized = normalizePathFragment(candidate);
155
+ if (!normalized || normalized.startsWith('..')) {
156
+ return;
157
+ }
158
+
159
+ payload.add(normalized);
160
+ });
161
+
162
+ return Array.from(payload);
163
+ }
164
+
165
+ function createIgnoreMatcher(patterns = []) {
166
+ if (!patterns.length) {
167
+ return () => false;
168
+ }
169
+
170
+ return function matcher(relativePath) {
171
+ const normalized = normalizePathFragment(relativePath);
172
+ if (!normalized) {
173
+ return false;
174
+ }
175
+
176
+ return patterns.some(pattern => normalized === pattern || normalized.startsWith(`${pattern}/`));
177
+ };
178
+ }
179
+
180
+ function formatDisplayPath(relativePath, fileName, isDirectory) {
181
+ const source = relativePath && relativePath !== '.' ? relativePath : fileName;
182
+ const normalized = (source || '').replace(/\\/g, '/');
183
+ if (!normalized) {
184
+ return isDirectory ? './' : '.';
185
+ }
186
+
187
+ const sanitized = normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;
188
+ return isDirectory ? `${sanitized}/` : sanitized;
189
+ }
190
+
37
191
  /**
38
192
  * 将首字母转换为小写
39
193
  * @param {string} text
@@ -119,15 +273,19 @@ function createIdGenerator(existingIds = new Set()) {
119
273
  * @param {Array} nodes
120
274
  * @param {(node: import('domhandler').Element) => void} visitor
121
275
  */
122
- function walkNodes(nodes = [], visitor) {
276
+ function walkNodes(nodes = [], visitor, options = {}) {
277
+ const { shouldSkipChildren = () => false } = options;
278
+
123
279
  nodes.forEach(node => {
124
- if (node.type === 'tag' && Array.isArray(node.children) && node.children.length) {
125
- walkNodes(node.children, visitor);
126
- } else if (node.children && node.children.length) {
127
- walkNodes(node.children, visitor);
280
+ const isTag = node.type === 'tag';
281
+ const hasChildren = Array.isArray(node.children) && node.children.length;
282
+ const skipChildren = isTag && shouldSkipChildren(node);
283
+
284
+ if (hasChildren && (!isTag || !skipChildren)) {
285
+ walkNodes(node.children, visitor, options);
128
286
  }
129
287
 
130
- if (node.type === 'tag') {
288
+ if (isTag) {
131
289
  visitor(node);
132
290
  }
133
291
  });
@@ -156,7 +314,7 @@ function isUpperCase(char) {
156
314
 
157
315
  function shouldSkipTag(tagName) {
158
316
  if (!tagName) return true;
159
- if (blacklistTags.includes(tagName)) return true;
317
+ if (notParseTags.includes(tagName)) return true;
160
318
  if (!isUpperCase(tagName[0])) return true;
161
319
  return false;
162
320
  }
@@ -170,6 +328,11 @@ function shouldSkipTag(tagName) {
170
328
  function addNodeId($, filter, generateId, body) {
171
329
  const roots = $.root().children().toArray();
172
330
  const insertions = [];
331
+ const options = {
332
+ shouldSkipChildren(node) {
333
+ return skipDescendantTags.has(node.name);
334
+ }
335
+ };
173
336
 
174
337
  walkNodes(roots, node => {
175
338
  const tagName = node.name;
@@ -186,7 +349,7 @@ function addNodeId($, filter, generateId, body) {
186
349
  attrs[attrKey] = newId;
187
350
  node.attribs = attrs;
188
351
  insertions.push(insertion);
189
- });
352
+ }, options);
190
353
 
191
354
  return insertions;
192
355
  }
@@ -340,6 +503,38 @@ function getIndentationForInsert(source, lineBreakIndex) {
340
503
  return match[0];
341
504
  }
342
505
 
506
+ /**
507
+ * 寻找起始标签对应的闭合符号,忽略属性值内的'>'
508
+ * @param {string} source
509
+ * @param {number} tagStart
510
+ * @returns {number}
511
+ */
512
+ function findTagCloseIndex(source, tagStart) {
513
+ let quote = null;
514
+
515
+ for (let i = tagStart; i < source.length; i += 1) {
516
+ const char = source[i];
517
+
518
+ if (quote) {
519
+ if (char === quote && source[i - 1] !== '\\') {
520
+ quote = null;
521
+ }
522
+ continue;
523
+ }
524
+
525
+ if (char === '"' || char === "'") {
526
+ quote = char;
527
+ continue;
528
+ }
529
+
530
+ if (char === '>') {
531
+ return i;
532
+ }
533
+ }
534
+
535
+ return -1;
536
+ }
537
+
343
538
  /**
344
539
  * 基于节点位置信息生成插入操作
345
540
  * @param {string} body
@@ -353,7 +548,7 @@ function createAttributeInsertion(body, node, newId) {
353
548
  }
354
549
 
355
550
  const tagStart = node.startIndex;
356
- const closeIndex = body.indexOf('>', tagStart);
551
+ const closeIndex = findTagCloseIndex(body, tagStart);
357
552
  if (closeIndex === -1) {
358
553
  return null;
359
554
  }
@@ -420,6 +615,10 @@ const filter = (attrs, tagName) => {
420
615
  return true;
421
616
  }
422
617
 
618
+ if (blacklistTags.includes(tagName)) {
619
+ return true;
620
+ }
621
+
423
622
  if (tagName.endsWith('Column') && tagName !== 'FlexColumn') {
424
623
  return !!attrs.Name || !!attrs.DataField;;
425
624
  }
@@ -434,12 +633,23 @@ function readGspxFile(filePath, basePath = webPath) {
434
633
  const fullPath = path.join(filePath, file);
435
634
  const relativePath = path.relative(basePath, fullPath);
436
635
 
437
- if (fs.lstatSync(fullPath).isDirectory()) {
636
+ const stat = fs.lstatSync(fullPath);
637
+ const isDirectory = stat.isDirectory();
638
+ const displayPath = formatDisplayPath(relativePath, file, isDirectory);
639
+
640
+ if (isDirectory) {
438
641
  if (blacklistDirs.includes(file)) {
439
- console.log(chalk.yellow(`跳过黑名单目录: ${relativePath}/`));
642
+ console.log(chalk.yellow(`跳过黑名单目录: ${displayPath}`));
440
643
  return;
441
644
  }
645
+ }
442
646
 
647
+ if (shouldIgnoreRelativePath(relativePath)) {
648
+ console.log(chalk.cyan(`忽略路径: ${displayPath}`));
649
+ return;
650
+ }
651
+
652
+ if (isDirectory) {
443
653
  readGspxFile(fullPath, basePath);
444
654
  } else if (file.endsWith('.gspx')) {
445
655
  const { $, xmlDeclaration, singleQuotedAttrs, body } = parseGspx(fullPath);
@@ -462,6 +672,18 @@ function readGspxFile(filePath, basePath = webPath) {
462
672
  }
463
673
 
464
674
  function start() {
675
+ const ignoreCandidates = Array.isArray(config.ignoreAutoId) && config.ignoreAutoId.length
676
+ ? config.ignoreAutoId
677
+ : Array.isArray(config.ignoreFiles)
678
+ ? config.ignoreFiles
679
+ : [];
680
+
681
+ normalizedIgnoreEntries = collectIgnoreEntries(ignoreCandidates, webPath);
682
+ shouldIgnoreRelativePath = createIgnoreMatcher(normalizedIgnoreEntries);
683
+
684
+ if (normalizedIgnoreEntries.length) {
685
+ console.log(chalk.cyan(`忽略规则: ${normalizedIgnoreEntries.join(', ')}`));
686
+ }
465
687
  readGspxFile(webPath);
466
688
  }
467
689
 
package/tool/checkjs.js CHANGED
@@ -1,3 +1,4 @@
1
+ var os = require('os');
1
2
  var utils = require('../lib/utils.js');
2
3
  var config = require('../lib/config.js');
3
4
  var fs = require('fs');
@@ -197,7 +198,6 @@ module.exports.start = async function() {
197
198
 
198
199
  // 生成检测报告 markdown语法
199
200
  var warnGspxCount = Object.keys(gspxData.warnList).length;
200
- var status = '无异常';
201
201
  var trimErrKeys = Object.keys(trimErrDatas);
202
202
  var errKeys = Object.keys(errDatas);
203
203
  var undefKeys = Object.keys(undefDatas);
@@ -208,9 +208,6 @@ module.exports.start = async function() {
208
208
  projectName: getModName(),
209
209
  branchName: getBranchName(),
210
210
  }
211
- if (errKeys.length > 0 || undefKeys.length > 0 || trimErrKeys.length > 0 || warnKeys.length > 0 || notUtf8List.length > 0 || warnGspxCount > 0 || gspxData.errorList.length > 0) {
212
- status = '有异常';
213
- }
214
211
 
215
212
  var branchName = getBranchName();
216
213
  var modName = getModName();
@@ -297,7 +294,7 @@ module.exports.start = async function() {
297
294
  webhookList.push(`1. for..in语法:${forinKeys.length}个,占比${calc(forinKeys.length, jsFiles.length)}`);
298
295
 
299
296
  // 随机显示5个异常文件到钉钉消息
300
- var errCount = reportData.data.errCount + reportData.data.undefCount;
297
+ var errCount = reportData.data.errorCount + reportData.data.undefCount;
301
298
  /*
302
299
  if (errCount > 0) {
303
300
  var list0 = [];
@@ -527,19 +524,16 @@ module.exports.start = async function() {
527
524
  postBaseData(reportData, gspxData); // 推送业务组件清单到基础数据
528
525
 
529
526
  // 推送到钉钉
530
- if (status != '无异常') { // 检查结果检查就不发到钉钉了; ps:健康也发到钉钉,鼓励开发继续保持;
531
- postWebhooks(id, reportUrl, webhookList);
532
- }
527
+ postWebhooks(id, reportUrl, webhookList);
533
528
 
534
- writeAbortFile(errCount);
529
+ writeAbortFile(errCount, reportUrl);
535
530
  }
536
531
 
537
- function writeAbortFile(errCount) {
538
- var os = require('os');
532
+ function writeAbortFile(errCount, reportUrl) {
539
533
  var s = path.join(os.tmpdir(), 'checkjsAbort');
540
534
  if (errCount > 0 && config.abortUrl) {
541
- var tips = '警告!!!代码检测发现异常,已终止当前构建任务。请修复代码后重新构建。';
542
- utils.postWeebhooks(tips);
535
+ var tips = `警告!!!代码检测发现异常,已终止当前构建任务。请修复代码后重新构建。[报告详情](${reportUrl})`;
536
+ utils.postWeebhooks(tips, true);
543
537
 
544
538
  console.log(tips);
545
539
  fs.writeFileSync(s, 'err');
package/tool/compress.js CHANGED
@@ -74,7 +74,7 @@ function checkJsHasZ(jsName) {
74
74
  console.log('检查压缩后的js里面是否有黑名单替换', crabaJs);
75
75
  if (contentJs.indexOf('_Z_') < 0) {
76
76
  var tips = '发版警告!!!upload craba失败!!!' + jsName + '文件的黑名单类名(_Z_)替换失败,终止打包和上传任务;请重试打包。';
77
- utils.postWeebhooks(tips);
77
+ utils.postWeebhooks(tips, true);
78
78
  return false;
79
79
  }
80
80
  return true;
@@ -90,7 +90,7 @@ function doUploadCraba(version) {
90
90
  console.log(response.data);
91
91
  if (version) doAutoTest();
92
92
  }).catch(function(err) {
93
- utils.postWeebhooks(config.modName + '上传资源失败');
93
+ utils.postWeebhooks(config.modName + '上传资源失败',true);
94
94
  console.log(err.message);
95
95
  });
96
96
  }
@@ -127,7 +127,7 @@ function doAutoTest() {
127
127
  }).then(function(response) {
128
128
 
129
129
  }).catch(function(err) {
130
- utils.postWeebhooks('调用自动化测试失败');
130
+ utils.postWeebhooks('调用自动化测试失败',true);
131
131
  console.log(err);
132
132
  });
133
133
  */
@@ -50,7 +50,7 @@ function doUpload() {
50
50
  console.log(response.data);
51
51
  doEnd();
52
52
  }).catch(function(err) {
53
- utils.postWeebhooks('shell皮肤发布失败');
53
+ utils.postWeebhooks('shell皮肤发布失败',true);
54
54
  console.log(err);
55
55
  });
56
56
  }
package/tool/start.js CHANGED
@@ -133,6 +133,7 @@ class Start {
133
133
  { name: '-localLogin', type: 'boolean' },
134
134
 
135
135
  { name: '-ignoreCheck', type: 'array' },
136
+ { name: '-ignoreAutoId', type: 'array' },
136
137
  { name: '-childModList', type: 'array' },
137
138
  { name: '-globals', type: 'array' },
138
139
 
@@ -431,4 +432,3 @@ async function waitFolder(question) {
431
432
  }
432
433
  return projectPath;
433
434
  }
434
-