mm_eslint 1.4.4 → 1.4.6
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 +56 -19
- package/README_EN.md +57 -20
- package/index.js +200 -1045
- package/{config.js → lib/config.js} +95 -30
- package/{corrector.js → lib/corrector.js} +54 -2
- package/lib/detector/class_instance_name.js +246 -0
- package/lib/detector/class_name.js +261 -0
- package/lib/detector/const_name.js +519 -0
- package/lib/detector/function_name.js +318 -0
- package/lib/detector/index.js +18 -0
- package/lib/detector/name.js +626 -0
- package/lib/detector/object_name.js +245 -0
- package/lib/detector/param_name.js +247 -0
- package/lib/detector/variable_name.js +286 -0
- package/lib/fix/class_fix.js +83 -0
- package/lib/fix/export_fix.js +169 -0
- package/lib/fix/function_fix.js +85 -0
- package/lib/fix/index.js +21 -0
- package/lib/fix/method_fix.js +82 -0
- package/lib/fix/param_fix.js +63 -0
- package/lib/fix/property_fix.js +93 -0
- package/lib/fix/variable_fix.js +134 -0
- package/lib/fix.js +160 -0
- package/lib/tip.js +85 -0
- package/{util.js → lib/util.js} +8 -0
- package/{validator.js → lib/validator.js} +12 -16
- package/package.json +13 -11
- package/detector.js +0 -1239
- package/eslint.config.js +0 -22
- package/tip.js +0 -71
|
@@ -15,6 +15,7 @@ class Config {
|
|
|
15
15
|
'class-instance': [],
|
|
16
16
|
'function': [],
|
|
17
17
|
'param': [],
|
|
18
|
+
'object': [],
|
|
18
19
|
'variable': [],
|
|
19
20
|
'constant': []
|
|
20
21
|
},
|
|
@@ -24,6 +25,7 @@ class Config {
|
|
|
24
25
|
'class-instance': [],
|
|
25
26
|
'function': [],
|
|
26
27
|
'param': [],
|
|
28
|
+
'object': [],
|
|
27
29
|
'variable': [],
|
|
28
30
|
'constant': []
|
|
29
31
|
},
|
|
@@ -33,6 +35,7 @@ class Config {
|
|
|
33
35
|
'class-instance': {},
|
|
34
36
|
'function': {},
|
|
35
37
|
'param': {},
|
|
38
|
+
'object': {},
|
|
36
39
|
'variable': {},
|
|
37
40
|
'constant': {}
|
|
38
41
|
}
|
|
@@ -47,8 +50,8 @@ class Config {
|
|
|
47
50
|
*/
|
|
48
51
|
Config.prototype.getRegex = function (style) {
|
|
49
52
|
var regex = {
|
|
50
|
-
"PascalCase": /^[A-Z][a-z]
|
|
51
|
-
"camelCase": /^[a-z][a-z]*([A-Z][a-z]*)*$/,
|
|
53
|
+
"PascalCase": /^[A-Z][a-z]+[A-Za-z]*$/,
|
|
54
|
+
"camelCase": /^[a-z][a-z]*([A-Z][a-z]*)*[a-z0-9]*$/,
|
|
52
55
|
"snake_case": /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/,
|
|
53
56
|
"UPPER_SNAKE_CASE": /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/,
|
|
54
57
|
"lowercase": /^[a-z]+([a-z0-9]+)*$/,
|
|
@@ -65,7 +68,7 @@ Config.prototype.getRegex = function (style) {
|
|
|
65
68
|
* @returns {Array} 禁止拼接词列表
|
|
66
69
|
*/
|
|
67
70
|
Config.prototype.getForbiddenWords = function (name_type) {
|
|
68
|
-
let type =
|
|
71
|
+
let type = this.getType(name_type);
|
|
69
72
|
var forbidden = {
|
|
70
73
|
'class': [
|
|
71
74
|
'manager',
|
|
@@ -103,6 +106,7 @@ Config.prototype.getForbiddenWords = function (name_type) {
|
|
|
103
106
|
'field',
|
|
104
107
|
'property',
|
|
105
108
|
'entity',
|
|
109
|
+
'data'
|
|
106
110
|
],
|
|
107
111
|
'function': [
|
|
108
112
|
'data',
|
|
@@ -149,10 +153,10 @@ Config.prototype.getForbiddenWords = function (name_type) {
|
|
|
149
153
|
'array',
|
|
150
154
|
'map',
|
|
151
155
|
'set',
|
|
152
|
-
'collection',
|
|
153
156
|
'container',
|
|
154
157
|
'instance',
|
|
155
158
|
'data',
|
|
159
|
+
'collection',
|
|
156
160
|
'item',
|
|
157
161
|
'items',
|
|
158
162
|
'element',
|
|
@@ -300,6 +304,14 @@ Config.prototype.getBaseRule = function (name_type) {
|
|
|
300
304
|
single_word: true,
|
|
301
305
|
single_word_len: 8
|
|
302
306
|
},
|
|
307
|
+
'object': {
|
|
308
|
+
name: '对象名',
|
|
309
|
+
min: 1,
|
|
310
|
+
max: 20,
|
|
311
|
+
styles: ['camelCase', 'snake_case'],
|
|
312
|
+
single_word: true,
|
|
313
|
+
single_word_len: 8
|
|
314
|
+
},
|
|
303
315
|
'variable': {
|
|
304
316
|
name: '变量名',
|
|
305
317
|
min: 1,
|
|
@@ -308,6 +320,14 @@ Config.prototype.getBaseRule = function (name_type) {
|
|
|
308
320
|
single_word: true,
|
|
309
321
|
single_word_len: 8
|
|
310
322
|
},
|
|
323
|
+
'property': {
|
|
324
|
+
name: '属性名',
|
|
325
|
+
min: 1,
|
|
326
|
+
max: 20,
|
|
327
|
+
styles: ['snake_case', 'camelCase', 'PascalCase'],
|
|
328
|
+
single_word: true,
|
|
329
|
+
single_word_len: 8
|
|
330
|
+
},
|
|
311
331
|
'constant': {
|
|
312
332
|
name: '常量名',
|
|
313
333
|
min: 1,
|
|
@@ -341,13 +361,7 @@ Config.prototype.getRule = function (name_type) {
|
|
|
341
361
|
* @returns {object} 推荐词映射
|
|
342
362
|
*/
|
|
343
363
|
Config.prototype.getRecommendWords = function (name_type) {
|
|
344
|
-
let type =
|
|
345
|
-
if (type == 'function') {
|
|
346
|
-
type = 'function';
|
|
347
|
-
}
|
|
348
|
-
else if (type == 'value') {
|
|
349
|
-
type = 'variable';
|
|
350
|
-
}
|
|
364
|
+
let type = this.getType(name_type);
|
|
351
365
|
var recommend = {
|
|
352
366
|
'class': {
|
|
353
367
|
'App': ['Application'],
|
|
@@ -460,17 +474,33 @@ Config.prototype.getRecommendWords = function (name_type) {
|
|
|
460
474
|
return {};
|
|
461
475
|
};
|
|
462
476
|
|
|
477
|
+
Config.prototype.getType = function (name_type) {
|
|
478
|
+
let type = name_type.replace('property-', '').replace('internal-', '').replace('private-', '').replace('internal-', '').replace('prototype-', '').replace('use-', '').replace('super-', '').replace('static-', '').replace('param-', '').replace('export-', '');
|
|
479
|
+
return type;
|
|
480
|
+
}
|
|
481
|
+
|
|
463
482
|
/**
|
|
464
483
|
* 获取忽略词列表
|
|
465
484
|
* @param {string} name_type 名称类型
|
|
466
485
|
* @returns {Array} 忽略词列表
|
|
467
486
|
*/
|
|
468
487
|
Config.prototype.getIgnoreWords = function (name_type) {
|
|
469
|
-
let type =
|
|
488
|
+
let type = this.getType(name_type);
|
|
470
489
|
var ignore = {
|
|
471
490
|
'class': [
|
|
472
491
|
'exports', 'Middleware', 'Component', 'Controller', 'Repository', 'Interface', 'Transformer', 'Template'
|
|
473
492
|
],
|
|
493
|
+
'class-instance': [
|
|
494
|
+
// JavaScript 内置全局对象(只保留不符合小驼峰规范的)
|
|
495
|
+
'JSON', 'Math', 'RegExp', 'EvalError', 'RangeError', 'ReferenceError',
|
|
496
|
+
'SyntaxError', 'TypeError', 'URIError', 'WeakMap', 'WeakSet', 'Promise',
|
|
497
|
+
'Proxy', 'Reflect', 'Intl', 'ArrayBuffer', 'DataView', 'Float32Array', 'Float64Array',
|
|
498
|
+
'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array',
|
|
499
|
+
'Uint8ClampedArray', 'BigInt64Array', 'BigUint64Array', 'Atomics', 'SharedArrayBuffer',
|
|
500
|
+
'URL', 'URLSearchParams', 'Headers', 'Request', 'Response', 'FormData', 'Blob', 'File',
|
|
501
|
+
'FileReader', 'Image', 'Audio', 'Video', 'Canvas', 'WebSocket', 'EventSource',
|
|
502
|
+
'XMLHttpRequest', 'Infinity', 'NaN', 'global'
|
|
503
|
+
],
|
|
474
504
|
'function': [
|
|
475
505
|
'constructor', 'prototype', 'hasOwnProperty',
|
|
476
506
|
'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString',
|
|
@@ -531,27 +561,62 @@ Config.prototype.getTypeNameMap = function () {
|
|
|
531
561
|
var type_name_map = {
|
|
532
562
|
'function': '函数',
|
|
533
563
|
'variable': '变量',
|
|
534
|
-
'
|
|
564
|
+
'object': '对象',
|
|
535
565
|
'class-instance': '类实例',
|
|
536
566
|
'class': '类',
|
|
537
567
|
'constant': '常量',
|
|
538
|
-
'
|
|
539
|
-
|
|
540
|
-
'property
|
|
541
|
-
'property-
|
|
542
|
-
'property-
|
|
543
|
-
'
|
|
544
|
-
'
|
|
545
|
-
'
|
|
546
|
-
'
|
|
547
|
-
|
|
548
|
-
'
|
|
549
|
-
'
|
|
550
|
-
'
|
|
551
|
-
'
|
|
552
|
-
'
|
|
553
|
-
'
|
|
554
|
-
|
|
568
|
+
'super-class': '继承-类',
|
|
569
|
+
// 属性
|
|
570
|
+
'property': '属性',
|
|
571
|
+
'property-function': '属性—函数',
|
|
572
|
+
'property-variable': '属性—变量',
|
|
573
|
+
'property-object': '属性—对象',
|
|
574
|
+
'property-class-instance': '属性—类实例',
|
|
575
|
+
'property-class': '属性—类',
|
|
576
|
+
'property-constant': '属性—常量',
|
|
577
|
+
// 静态属性
|
|
578
|
+
'static-function': '静态属性—函数',
|
|
579
|
+
'static-variable': '静态属性—变量',
|
|
580
|
+
'static-object': '静态属性—对象',
|
|
581
|
+
'static-class-instance': '静态属性—类实例',
|
|
582
|
+
'static-class': '静态属性—类',
|
|
583
|
+
'static-constant': '静态属性—常量',
|
|
584
|
+
// 原型属性
|
|
585
|
+
'prototype-class-instance': '原型属性—类实例',
|
|
586
|
+
'prototype-function': '原型属性—函数',
|
|
587
|
+
'prototype-variable': '原型属性—变量',
|
|
588
|
+
'prototype-object': '原型属性—对象',
|
|
589
|
+
// 私有属性
|
|
590
|
+
'private-function': '私有属性—函数',
|
|
591
|
+
'private-variable': '私有属性—变量',
|
|
592
|
+
'private-object': '私有属性—对象',
|
|
593
|
+
'private-class-instance': '私有属性—类实例',
|
|
594
|
+
// 内部属性
|
|
595
|
+
'internal-function': '内部属性—函数',
|
|
596
|
+
'internal-variable': '内部属性—变量',
|
|
597
|
+
'internal-object': '内部属性—对象',
|
|
598
|
+
'internal-class-instance': '内部属性—类实例',
|
|
599
|
+
// 引用
|
|
600
|
+
'use-variable': '引用-变量',
|
|
601
|
+
'use-function': '引用-函数',
|
|
602
|
+
'use-object': '引用-对象',
|
|
603
|
+
'use-class': '引用-类',
|
|
604
|
+
'use-constant': '引用-常量',
|
|
605
|
+
'use-class-instance': '引用-类实例',
|
|
606
|
+
// 参数
|
|
607
|
+
'param': '参数',
|
|
608
|
+
'param-class': '参数—类',
|
|
609
|
+
'param-class-instance': '参数—类实例',
|
|
610
|
+
'param-variable': '参数—变量',
|
|
611
|
+
'param-object': '参数—对象',
|
|
612
|
+
'param-function': '参数—函数',
|
|
613
|
+
// 导出
|
|
614
|
+
'export-class': '导出-类',
|
|
615
|
+
'export-class-instance': '导出-类实例',
|
|
616
|
+
'export-variable': '导出-变量',
|
|
617
|
+
'export-object': '导出-对象',
|
|
618
|
+
'export-function': '导出-函数',
|
|
619
|
+
'export-constant': '导出-常量'
|
|
555
620
|
};
|
|
556
621
|
return type_name_map;
|
|
557
622
|
}
|
|
@@ -43,7 +43,15 @@ Corrector.prototype.getSuggestion = function (error) {
|
|
|
43
43
|
} catch {
|
|
44
44
|
return error.name;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
|
|
47
|
+
var suggestion = error.prefix + word;
|
|
48
|
+
|
|
49
|
+
// 检查建议名称是否与原名称相同,如果相同则不显示建议
|
|
50
|
+
if (suggestion === error.name || word === error.name) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return suggestion;
|
|
47
55
|
};
|
|
48
56
|
|
|
49
57
|
/**
|
|
@@ -273,9 +281,54 @@ Corrector.prototype._filterForbiddenWord = function (words, forbidden_words) {
|
|
|
273
281
|
new_word.push(word);
|
|
274
282
|
}
|
|
275
283
|
}
|
|
284
|
+
if (new_word.length === 0) {
|
|
285
|
+
new_word.push(words[words.length - 1]);
|
|
286
|
+
}
|
|
276
287
|
return new_word;
|
|
277
288
|
}
|
|
278
289
|
|
|
290
|
+
/**
|
|
291
|
+
* 替换禁止词汇为推荐词汇
|
|
292
|
+
* @param {string[]} words 单词数组
|
|
293
|
+
* @param {Array} forbidden_words 禁止词列表
|
|
294
|
+
* @param {object} recommend_words 推荐词配置
|
|
295
|
+
* @returns {string[]} 替换后的单词数组
|
|
296
|
+
*/
|
|
297
|
+
Corrector.prototype._replaceForbiddenWords = function (words, forbidden_words, recommend_words) {
|
|
298
|
+
var new_words = [];
|
|
299
|
+
for (let i = 0; i < words.length; i++) {
|
|
300
|
+
let word = words[i];
|
|
301
|
+
if (this._isForbiddenWord(word, forbidden_words)) {
|
|
302
|
+
// 尝试为禁用词找到替代建议
|
|
303
|
+
var replacement = this._getForbiddenWordReplacement(word, recommend_words);
|
|
304
|
+
if (replacement) {
|
|
305
|
+
new_words.push(replacement);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
new_words.push(word);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return new_words;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* 获取禁用词的替代建议
|
|
316
|
+
* @param {string} forbidden_word 禁用词
|
|
317
|
+
* @param {object} recommend_words 推荐词配置
|
|
318
|
+
* @returns {string} 替代建议词
|
|
319
|
+
*/
|
|
320
|
+
Corrector.prototype._getForbiddenWordReplacement = function (forbidden_word, recommend_words) {
|
|
321
|
+
for (var replacement in recommend_words) {
|
|
322
|
+
var forbidden_list = recommend_words[replacement];
|
|
323
|
+
for (let i = 0; i < forbidden_list.length; i++) {
|
|
324
|
+
if (forbidden_word.toLowerCase() === forbidden_list[i].toLowerCase()) {
|
|
325
|
+
return replacement;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
|
|
279
332
|
/**
|
|
280
333
|
* 获取推荐的单词
|
|
281
334
|
* @param {string[]} words 单词数组
|
|
@@ -284,7 +337,6 @@ Corrector.prototype._filterForbiddenWord = function (words, forbidden_words) {
|
|
|
284
337
|
* @returns {string[]} 推荐的单词数组
|
|
285
338
|
*/
|
|
286
339
|
Corrector.prototype._toRecommendWords = function (words, recommend, single_word_len) {
|
|
287
|
-
// console.log('推荐单词', words, recommend, single_word_len);
|
|
288
340
|
let recommend_words = [];
|
|
289
341
|
for (let i = 0; i < words.length; i++) {
|
|
290
342
|
let word = words[i];
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
const { Name } = require('./name.js');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 类实例名检测器类
|
|
5
|
+
* 负责检测类实例名是否符合命名规范
|
|
6
|
+
*/
|
|
7
|
+
class ClassInstanceName extends Name {
|
|
8
|
+
constructor(context, config) {
|
|
9
|
+
super(context, config);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 变量声明节点检测函数
|
|
15
|
+
* @param {Object} node - 变量声明节点
|
|
16
|
+
* @returns {Object|undefined} - 检测结果对象或undefined
|
|
17
|
+
*/
|
|
18
|
+
ClassInstanceName.prototype.VariableDeclaration = function (node) {
|
|
19
|
+
// 判断右边是否有 new 表达式
|
|
20
|
+
if (node.declarations[0].init && node.declarations[0].init.type === 'NewExpression') {
|
|
21
|
+
let name = node.declarations[0].id.name;
|
|
22
|
+
let original_type = 'class-instance';
|
|
23
|
+
let error = this.check(node.declarations[0].id, name, original_type);
|
|
24
|
+
this.report(node.declarations[0].id, error, original_type);
|
|
25
|
+
return error;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 判断右边是否有正则表达式字面量(RegExp实例)
|
|
29
|
+
if (node.declarations[0].init && node.declarations[0].init.type === 'Literal' &&
|
|
30
|
+
node.declarations[0].init.regex) {
|
|
31
|
+
let name = node.declarations[0].id.name;
|
|
32
|
+
let original_type = 'class-instance';
|
|
33
|
+
let error = this.check(node.declarations[0].id, name, original_type);
|
|
34
|
+
this.report(node.declarations[0].id, error, original_type);
|
|
35
|
+
return error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 判断右边是否直接赋值类实例名
|
|
39
|
+
if (node.declarations[0].init && node.declarations[0].init.type === 'Identifier') {
|
|
40
|
+
// 根据右边值的类型决定左边变量的类型
|
|
41
|
+
let original_type = this._getOriginalType(node.declarations[0], 'VariableDeclaration');
|
|
42
|
+
|
|
43
|
+
// 如果右边是类实例,左边变量应该按照类实例的命名规范检测(小驼峰)
|
|
44
|
+
if (original_type === 'class-instance') {
|
|
45
|
+
let name = node.declarations[0].id.name;
|
|
46
|
+
let error = this.check(node.declarations[0].id, name, 'class-instance');
|
|
47
|
+
this.report(node.declarations[0].id, error, 'class-instance');
|
|
48
|
+
return error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 属性节点检测函数
|
|
55
|
+
* @param {Object} node - 属性节点
|
|
56
|
+
* @returns {Object|undefined} - 检测结果对象或undefined
|
|
57
|
+
*/
|
|
58
|
+
ClassInstanceName.prototype.Property = function (node) {
|
|
59
|
+
// 判断右边是否有 new 表达式
|
|
60
|
+
if (node.value.type === 'NewExpression') {
|
|
61
|
+
let name = node.key.name;
|
|
62
|
+
let original_type = 'property-class-instance';
|
|
63
|
+
let error = this.check(node, name, original_type);
|
|
64
|
+
this.report(node, error, original_type);
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 判断右边是否直接定义为类实例名
|
|
69
|
+
if (node.value.type === 'Identifier') {
|
|
70
|
+
// 根据右边值的类型决定左边属性的类型
|
|
71
|
+
let original_type = this._getOriginalType(node, 'Property');
|
|
72
|
+
|
|
73
|
+
// 如果右边是类实例,左边属性应该按照类实例的命名规范检测(小驼峰)
|
|
74
|
+
if (original_type === 'class-instance') {
|
|
75
|
+
let name = node.key.name;
|
|
76
|
+
let error = this.check(node, name, 'property-class-instance');
|
|
77
|
+
this.report(node, error, 'property-class-instance');
|
|
78
|
+
return error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 类属性定义节点检测函数
|
|
85
|
+
* @param {Object} node - 类属性定义节点
|
|
86
|
+
* @returns {Object|undefined} - 检测结果对象或undefined
|
|
87
|
+
*/
|
|
88
|
+
ClassInstanceName.prototype.PropertyDefinition = function (node) {
|
|
89
|
+
// 判断右边是否有 new 表达式
|
|
90
|
+
if (node.value && node.value.type === 'NewExpression') {
|
|
91
|
+
let name = node.key.name;
|
|
92
|
+
let original_type = 'property-class-instance';
|
|
93
|
+
if (node.static) {
|
|
94
|
+
original_type = 'static-class-instance';
|
|
95
|
+
}
|
|
96
|
+
else if (node.key.type === 'PrivateIdentifier') {
|
|
97
|
+
original_type = 'private-class-instance';
|
|
98
|
+
}
|
|
99
|
+
else if (name.startsWith('_')) {
|
|
100
|
+
original_type = 'internal-class-instance';
|
|
101
|
+
}
|
|
102
|
+
let error = this.check(node, name, original_type);
|
|
103
|
+
this.report(node, error, original_type);
|
|
104
|
+
return error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 判断右边是否直接定义为类实例名
|
|
108
|
+
if (node.value && node.value.type === 'Identifier') {
|
|
109
|
+
// 根据右边值的类型决定左边属性的类型
|
|
110
|
+
let original_type = this._getOriginalType(node, 'PropertyDefinition');
|
|
111
|
+
|
|
112
|
+
// 如果右边是类实例,左边属性应该按照类实例的命名规范检测(小驼峰)
|
|
113
|
+
if (original_type === 'class-instance') {
|
|
114
|
+
let name = node.key.name;
|
|
115
|
+
let original_type = 'property-class-instance';
|
|
116
|
+
if (node.static) {
|
|
117
|
+
original_type = 'static-class-instance';
|
|
118
|
+
}
|
|
119
|
+
else if (node.key.type === 'PrivateIdentifier') {
|
|
120
|
+
original_type = 'private-class-instance';
|
|
121
|
+
}
|
|
122
|
+
else if (name.startsWith('_')) {
|
|
123
|
+
original_type = 'internal-class-instance';
|
|
124
|
+
}
|
|
125
|
+
let error = this.check(node, name, original_type);
|
|
126
|
+
this.report(node, error, original_type);
|
|
127
|
+
return error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 赋值表达式节点检测函数
|
|
134
|
+
* @param {Object} node - 赋值表达式节点
|
|
135
|
+
* @returns {Object|undefined} - 检测结果对象或undefined
|
|
136
|
+
*/
|
|
137
|
+
ClassInstanceName.prototype.AssignmentExpression = function (node) {
|
|
138
|
+
// 检测 exports 或 module.exports 赋值:exports.classInstance = classInstance;
|
|
139
|
+
// 排除右边是对象字面量的情况,交由Property检测器处理
|
|
140
|
+
if (node.operator === '=' && node.left && node.left.type === 'MemberExpression' &&
|
|
141
|
+
node.right && node.right.type !== 'ObjectExpression') {
|
|
142
|
+
// 检查左边是否为 exports 或 module.exports
|
|
143
|
+
let left_object = node.left.object;
|
|
144
|
+
let is_exports = false;
|
|
145
|
+
|
|
146
|
+
if (left_object.type === 'Identifier' && left_object.name === 'exports') {
|
|
147
|
+
is_exports = true;
|
|
148
|
+
} else if (left_object.type === 'MemberExpression' &&
|
|
149
|
+
left_object.object.type === 'Identifier' &&
|
|
150
|
+
left_object.object.name === 'module' &&
|
|
151
|
+
left_object.property.type === 'Identifier' &&
|
|
152
|
+
left_object.property.name === 'exports') {
|
|
153
|
+
is_exports = true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (is_exports && node.right && node.right.type === 'Identifier') {
|
|
157
|
+
// 先判断右边是否为类实例
|
|
158
|
+
let original_type = this._getOriginalType(node, 'AssignmentExpression');
|
|
159
|
+
|
|
160
|
+
// 如果右边是类实例,才检测左边的属性名
|
|
161
|
+
if (original_type === 'class-instance') {
|
|
162
|
+
let left_property = node.left.property;
|
|
163
|
+
if (left_property && left_property.type === 'Identifier') {
|
|
164
|
+
let name = left_property.name;
|
|
165
|
+
let error = this.check(left_property, name, 'property-class-instance');
|
|
166
|
+
this.report(left_property, error, 'property-class-instance');
|
|
167
|
+
return error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 默认导出节点检测函数
|
|
176
|
+
* @param {Object} node - 默认导出节点
|
|
177
|
+
* @returns {Object|undefined} - 检测结果对象或undefined
|
|
178
|
+
*/
|
|
179
|
+
ClassInstanceName.prototype.ExportDefaultDeclaration = function (node) {
|
|
180
|
+
// 检测导出的类实例名
|
|
181
|
+
if (node.declaration && node.declaration.type === 'Identifier') {
|
|
182
|
+
let name = node.declaration.name;
|
|
183
|
+
let type = this._getOriginalType(node.declaration, 'ExportDefaultDeclaration');
|
|
184
|
+
if (type === 'class-instance') {
|
|
185
|
+
let original_type = 'export-class-instance';
|
|
186
|
+
let error = this.check(node.declaration, name, original_type);
|
|
187
|
+
this.report(node.declaration, error, original_type);
|
|
188
|
+
return error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 命名导出节点检测函数
|
|
195
|
+
* @param {Object} node - 命名导出节点
|
|
196
|
+
* @returns {Object|undefined} - 检测结果对象或undefined
|
|
197
|
+
*/
|
|
198
|
+
ClassInstanceName.prototype.ExportNamedDeclaration = function (node) {
|
|
199
|
+
// 检测导出的类实例声明(如 export const myInstance = new ClassName())
|
|
200
|
+
if (node.declaration && node.declaration.type === 'VariableDeclaration') {
|
|
201
|
+
for (let i = 0; i < node.declaration.declarations.length; i++) {
|
|
202
|
+
let declaration = node.declaration.declarations[i];
|
|
203
|
+
if (declaration.init && declaration.init.type === 'NewExpression') {
|
|
204
|
+
let name = declaration.id.name;
|
|
205
|
+
let error = this.check(node, name, 'export-class-instance');
|
|
206
|
+
this.report(node, error, 'export-class-instance');
|
|
207
|
+
return error;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 检测命名导出的类实例引用
|
|
213
|
+
if (node.specifiers && node.specifiers.length > 0) {
|
|
214
|
+
for (let i = 0; i < node.specifiers.length; i++) {
|
|
215
|
+
let specifier = node.specifiers[i];
|
|
216
|
+
|
|
217
|
+
// 检测导出的标识符(避免重复检测)
|
|
218
|
+
// 优先检测 exported 属性,如果存在则使用它,否则使用 local 属性
|
|
219
|
+
let identifier_to_check = null;
|
|
220
|
+
let identifier_name = '';
|
|
221
|
+
|
|
222
|
+
if (specifier.exported && specifier.exported.type === 'Identifier') {
|
|
223
|
+
identifier_to_check = specifier.exported;
|
|
224
|
+
identifier_name = specifier.exported.name;
|
|
225
|
+
} else if (specifier.local && specifier.local.type === 'Identifier') {
|
|
226
|
+
identifier_to_check = specifier.local;
|
|
227
|
+
identifier_name = specifier.local.name;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 只有当找到有效标识符时才进行检测
|
|
231
|
+
if (identifier_to_check) {
|
|
232
|
+
let original_type = this._getOriginalType(specifier, 'ExportSpecifier');
|
|
233
|
+
|
|
234
|
+
// 只有当导出的是类实例时才检测
|
|
235
|
+
if (original_type === 'class-instance') {
|
|
236
|
+
let error = this.check(identifier_to_check, identifier_name, 'export-class-instance');
|
|
237
|
+
this.report(identifier_to_check, error, 'export-class-instance');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = {
|
|
245
|
+
ClassInstanceName
|
|
246
|
+
};
|