mm_eslint 1.4.3 → 1.4.5
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 +336 -0
- package/README_EN.md +336 -0
- package/config.js +626 -573
- package/corrector.js +375 -0
- package/detector.js +1040 -3413
- package/eslint.config.js +25 -0
- package/fix.js +441 -0
- package/handler.js +993 -0
- package/index.js +211 -1164
- package/package.json +59 -49
- package/tip.js +85 -0
- package/util.js +241 -0
- package/validator.js +266 -0
package/detector.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
const { Validator } = require('./validator');
|
|
2
|
+
const { Corrector } = require('./corrector');
|
|
3
|
+
const { Tip } = require('./tip');
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
|
-
*
|
|
6
|
+
* 类型检测器类
|
|
7
|
+
* 负责根据AST节点推断命名类型
|
|
3
8
|
*/
|
|
4
9
|
class Detector {
|
|
5
10
|
/**
|
|
@@ -7,3658 +12,1280 @@ class Detector {
|
|
|
7
12
|
* @param {object} config 配置对象
|
|
8
13
|
*/
|
|
9
14
|
constructor(config) {
|
|
10
|
-
// 直接将传入的配置对象赋值,确保原型方法不被丢失
|
|
11
15
|
this.config = config;
|
|
16
|
+
this.validator = new Validator(config);
|
|
17
|
+
this.corrector = new Corrector(config);
|
|
18
|
+
this.tip = new Tip(config);
|
|
19
|
+
}
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
21
|
+
/**
|
|
22
|
+
* 检测类声明类型
|
|
23
|
+
* @param {object} node AST节点
|
|
24
|
+
* @returns {string} 类型名称
|
|
25
|
+
*/
|
|
26
|
+
detectClassType(node) {
|
|
27
|
+
if (node.type === 'ClassDeclaration') {
|
|
28
|
+
return 'class';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (node.type === 'ClassExpression') {
|
|
32
|
+
return 'class';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (node.type === 'VariableDeclaration' &&
|
|
36
|
+
node.declarations &&
|
|
37
|
+
node.declarations.length > 0 &&
|
|
38
|
+
node.declarations[0].init &&
|
|
39
|
+
node.declarations[0].init.type === 'ClassExpression') {
|
|
40
|
+
return 'class';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (node.type === 'AssignmentExpression' && node.right && node.right.type === 'ClassExpression') {
|
|
44
|
+
return 'class';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (node.type === 'Property' && node.value && node.value.type === 'ClassExpression') {
|
|
48
|
+
return 'property-class';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 处理 module.exports = class user_manager {} 这种形式
|
|
52
|
+
if (node.type === 'AssignmentExpression' &&
|
|
53
|
+
node.left &&
|
|
54
|
+
node.left.type === 'MemberExpression' &&
|
|
55
|
+
node.left.object &&
|
|
56
|
+
node.left.object.type === 'Identifier' &&
|
|
57
|
+
node.left.object.name === 'module' &&
|
|
58
|
+
node.left.property &&
|
|
59
|
+
node.left.property.type === 'Identifier' &&
|
|
60
|
+
node.left.property.name === 'exports' &&
|
|
61
|
+
node.right &&
|
|
62
|
+
node.right.type === 'ClassExpression') {
|
|
63
|
+
return 'class';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 处理 module.exports = { Admin: Admin, AdminManager } 这种形式的引用
|
|
67
|
+
if ((node.type === 'Property' || node.type === 'ObjectProperty') && node.key && node.key.type === 'Identifier') {
|
|
68
|
+
// 检查是否在导出语句的上下文中
|
|
69
|
+
var parent = node.parent;
|
|
70
|
+
if (parent && parent.type === 'ObjectExpression') {
|
|
71
|
+
var grand_parent = parent.parent;
|
|
72
|
+
if (grand_parent && grand_parent.type === 'AssignmentExpression' &&
|
|
73
|
+
grand_parent.left && grand_parent.left.type === 'MemberExpression' &&
|
|
74
|
+
grand_parent.left.object && grand_parent.left.object.type === 'Identifier' &&
|
|
75
|
+
grand_parent.left.object.name === 'module' &&
|
|
76
|
+
grand_parent.left.property && grand_parent.left.property.type === 'Identifier' &&
|
|
77
|
+
grand_parent.left.property.name === 'exports') {
|
|
78
|
+
|
|
79
|
+
// 通用类型溯源:对于导出语句中的属性,溯源其引用的类型
|
|
80
|
+
var traced_type = this._traceReferenceType(node);
|
|
81
|
+
if (traced_type) {
|
|
82
|
+
return traced_type;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 如果无法溯源到具体类型,返回null让其他检测器处理
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null;
|
|
26
92
|
}
|
|
27
|
-
}
|
|
28
93
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
94
|
+
/**
|
|
95
|
+
* 检测类实例类型
|
|
96
|
+
* @param {object} node AST节点
|
|
97
|
+
* @returns {string} 类型名称
|
|
98
|
+
*/
|
|
99
|
+
detectClassInstanceType(node) {
|
|
100
|
+
// VariableDeclaration 中的 new 表达式
|
|
101
|
+
if (node.type === 'VariableDeclaration') {
|
|
102
|
+
if (node.declarations && node.declarations.length > 0) {
|
|
103
|
+
var decl = node.declarations[0];
|
|
104
|
+
if (decl.init && decl.init.type === 'NewExpression') {
|
|
105
|
+
return 'class-instance';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
40
108
|
}
|
|
41
109
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return null;
|
|
110
|
+
if (node.type === 'Property' && node.value && node.value.type === 'NewExpression') {
|
|
111
|
+
return 'property-class-instance';
|
|
45
112
|
}
|
|
46
113
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
56
|
-
class_name,
|
|
57
|
-
"class-name",
|
|
58
|
-
);
|
|
59
|
-
var error_message = style_err;
|
|
60
|
-
if (smart_abbreviation && smart_abbreviation !== class_name) {
|
|
61
|
-
error_message = style_err + ",建议使用: " + smart_abbreviation;
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
node: node,
|
|
65
|
-
message: error_message,
|
|
66
|
-
rule_type: "class-name",
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 检查并推荐更合适的词汇
|
|
71
|
-
var rec_err = this._checkAndRecommend(class_name, "class-name");
|
|
72
|
-
if (rec_err) {
|
|
73
|
-
// 生成完整的建议名称
|
|
74
|
-
var recommended_full_name = class_name.replace(
|
|
75
|
-
new RegExp(rec_err.original_word, "i"),
|
|
76
|
-
rec_err.recommended_word,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
node: node,
|
|
81
|
-
message: "类名'" + class_name + "',建议使用: " + recommended_full_name,
|
|
82
|
-
rule_type: "class-name",
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 检查长度
|
|
87
|
-
var len_err = this._checkNameLength(
|
|
88
|
-
class_name,
|
|
89
|
-
rule.min,
|
|
90
|
-
rule.max,
|
|
91
|
-
"class-name",
|
|
92
|
-
);
|
|
93
|
-
if (len_err) {
|
|
94
|
-
// 使用智能缩写生成推荐名称
|
|
95
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
96
|
-
class_name,
|
|
97
|
-
"class-name",
|
|
98
|
-
);
|
|
99
|
-
var error_message = len_err;
|
|
100
|
-
if (smart_abbreviation && smart_abbreviation !== class_name) {
|
|
101
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
114
|
+
if (node.type === 'AssignmentExpression' && node.right && node.right.type === 'NewExpression') {
|
|
115
|
+
return 'property-class-instance';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (node.type === 'PropertyDefinition' && node.key && node.value && node.value.type === 'NewExpression') {
|
|
119
|
+
// 判断是否是私有字段
|
|
120
|
+
if (node.key.type === 'PrivateIdentifier') {
|
|
121
|
+
return 'private-class-instance';
|
|
102
122
|
}
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class_name,
|
|
113
|
-
rule.single_word_len,
|
|
114
|
-
"class-name",
|
|
115
|
-
);
|
|
116
|
-
if (word_err) {
|
|
117
|
-
// 检查错误消息是否已经包含建议
|
|
118
|
-
var error_message = word_err;
|
|
119
|
-
if (!word_err.includes("建议使用")) {
|
|
120
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
121
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
122
|
-
class_name,
|
|
123
|
-
"class-name",
|
|
124
|
-
);
|
|
125
|
-
if (smart_abbreviation && smart_abbreviation !== class_name) {
|
|
126
|
-
error_message = word_err + ",建议使用: " + smart_abbreviation;
|
|
123
|
+
return node.static ? 'static-class-instance' : 'property-class-instance';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 正则表达式字面量视为类实例
|
|
127
|
+
if (node.type === 'VariableDeclaration') {
|
|
128
|
+
if (node.declarations && node.declarations.length > 0) {
|
|
129
|
+
var decl = node.declarations[0];
|
|
130
|
+
if (decl.init && decl.init.type === 'Literal' && decl.init.regex) {
|
|
131
|
+
return 'class-instance';
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
node: node,
|
|
132
|
-
message: error_message,
|
|
133
|
-
rule_type: "class-name",
|
|
134
|
-
};
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (forbid_err) {
|
|
140
|
-
return {
|
|
141
|
-
node: node,
|
|
142
|
-
message: forbid_err,
|
|
143
|
-
rule_type: "class-name",
|
|
144
|
-
};
|
|
136
|
+
if (node.type === 'Property' && node.value && node.value.type === 'Literal' && node.value.regex) {
|
|
137
|
+
return 'property-class-instance';
|
|
145
138
|
}
|
|
146
139
|
|
|
147
|
-
return null;
|
|
148
|
-
} catch (error) {
|
|
149
|
-
console.error("检测类名时出错:", error);
|
|
150
140
|
return null;
|
|
151
141
|
}
|
|
152
|
-
};
|
|
153
142
|
|
|
154
|
-
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
var rule = this.config.getRule("method-name");
|
|
163
|
-
if (!rule) {
|
|
164
|
-
return null;
|
|
143
|
+
/**
|
|
144
|
+
* 检测函数类型
|
|
145
|
+
* @param {object} node AST节点
|
|
146
|
+
* @returns {string} 类型名称
|
|
147
|
+
*/
|
|
148
|
+
detectFunctionType(node) {
|
|
149
|
+
if (node.type === 'FunctionDeclaration') {
|
|
150
|
+
return 'function';
|
|
165
151
|
}
|
|
166
152
|
|
|
167
|
-
//
|
|
168
|
-
if (
|
|
169
|
-
return
|
|
153
|
+
// 检测函数表达式内部的函数名
|
|
154
|
+
if (node.type === 'FunctionExpression' && node.id && node.id.type === 'Identifier') {
|
|
155
|
+
return 'function';
|
|
170
156
|
}
|
|
171
157
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
style_err = this._validate_naming_style(
|
|
177
|
-
method_name,
|
|
178
|
-
["_camelCase"],
|
|
179
|
-
"method-name",
|
|
180
|
-
);
|
|
181
|
-
} else {
|
|
182
|
-
// 公开方法:检查是否符合公开方法命名规范
|
|
183
|
-
style_err = this._validate_naming_style(
|
|
184
|
-
method_name,
|
|
185
|
-
rule.styles,
|
|
186
|
-
"method-name",
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (style_err) {
|
|
191
|
-
// 使用智能缩写生成推荐名称
|
|
192
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
193
|
-
method_name,
|
|
194
|
-
"method-name",
|
|
195
|
-
);
|
|
196
|
-
var error_message = style_err;
|
|
197
|
-
if (smart_abbreviation && smart_abbreviation !== method_name) {
|
|
198
|
-
error_message = style_err + ",建议使用: " + smart_abbreviation;
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
node: node,
|
|
202
|
-
message: error_message,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 检查长度
|
|
207
|
-
var len_err = this._checkNameLength(
|
|
208
|
-
method_name,
|
|
209
|
-
rule.min,
|
|
210
|
-
rule.max,
|
|
211
|
-
"method-name",
|
|
212
|
-
);
|
|
213
|
-
if (len_err) {
|
|
214
|
-
// 使用智能缩写生成推荐名称
|
|
215
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
216
|
-
method_name,
|
|
217
|
-
"method-name",
|
|
218
|
-
);
|
|
219
|
-
var error_message = len_err;
|
|
220
|
-
if (smart_abbreviation && smart_abbreviation !== method_name) {
|
|
221
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
158
|
+
if (node.type === 'VariableDeclaration' && node.declarations && node.declarations.length > 0) {
|
|
159
|
+
var decl = node.declarations[0];
|
|
160
|
+
if (decl.init && (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression')) {
|
|
161
|
+
return 'function';
|
|
222
162
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
241
|
-
method_name,
|
|
242
|
-
"method-name",
|
|
243
|
-
);
|
|
244
|
-
if (smart_abbreviation && smart_abbreviation !== method_name) {
|
|
245
|
-
error_message = word_err + ",建议使用: " + smart_abbreviation;
|
|
246
|
-
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 检测VariableDeclarator节点中的箭头函数
|
|
166
|
+
if (node.type === 'VariableDeclarator' && node.init &&
|
|
167
|
+
(node.init.type === 'FunctionExpression' || node.init.type === 'ArrowFunctionExpression')) {
|
|
168
|
+
return 'function';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (node.type === 'Property' && node.value &&
|
|
172
|
+
(node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
173
|
+
return 'property-function';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (node.type === 'MethodDefinition') {
|
|
177
|
+
// 检测私有方法(ES6私有方法语法 # 前缀)
|
|
178
|
+
if (node.key && node.key.type === 'PrivateIdentifier') {
|
|
179
|
+
return 'private-function';
|
|
247
180
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// 检查禁止词汇
|
|
255
|
-
var forbid_err = this._checkForbiddenWords(method_name, "method-name");
|
|
256
|
-
if (forbid_err) {
|
|
257
|
-
// 使用智能缩写生成推荐名称
|
|
258
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
259
|
-
method_name,
|
|
260
|
-
"method-name",
|
|
261
|
-
);
|
|
262
|
-
var error_message = forbid_err;
|
|
263
|
-
if (smart_abbreviation && smart_abbreviation !== method_name) {
|
|
264
|
-
error_message = forbid_err + ",建议使用: " + smart_abbreviation;
|
|
181
|
+
|
|
182
|
+
// 检测传统私有方法约定(_ 前缀)
|
|
183
|
+
if (node.key && node.key.type === 'Identifier' && node.key.name.startsWith('_')) {
|
|
184
|
+
return node.static ? 'static-function' : 'internal-function';
|
|
265
185
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
"'不是行业通用名,建议使用: " +
|
|
284
|
-
smart_abbreviation,
|
|
285
|
-
fix: function (fixer) {
|
|
286
|
-
return fixer.replaceText(node, smart_abbreviation);
|
|
287
|
-
},
|
|
288
|
-
};
|
|
186
|
+
|
|
187
|
+
return node.static ? 'static-function' : 'function';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (node.type === 'PropertyDefinition' && node.value &&
|
|
191
|
+
(node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
192
|
+
return node.static ? 'static-function' : 'property-function';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (node.type === 'AssignmentExpression' && node.right &&
|
|
196
|
+
(node.right.type === 'FunctionExpression' || node.right.type === 'ArrowFunctionExpression')) {
|
|
197
|
+
return 'property-function';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (node.type === 'CallExpression' && node.callee && node.callee.type === 'Identifier') {
|
|
201
|
+
// 函数调用应该被识别为函数使用,而不是函数定义
|
|
202
|
+
return 'use-function';
|
|
289
203
|
}
|
|
290
204
|
|
|
291
|
-
return null;
|
|
292
|
-
} catch (error) {
|
|
293
|
-
console.error("检测方法名时出错:", error);
|
|
294
205
|
return null;
|
|
295
206
|
}
|
|
296
|
-
};
|
|
297
207
|
|
|
298
|
-
/**
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (!rule) {
|
|
308
|
-
return null;
|
|
208
|
+
/**
|
|
209
|
+
* 检测变量类型
|
|
210
|
+
* @param {object} node AST节点
|
|
211
|
+
* @returns {string} 类型名称
|
|
212
|
+
*/
|
|
213
|
+
detectVariableType(node) {
|
|
214
|
+
// let/var 声明
|
|
215
|
+
if (node.type === 'VariableDeclaration' && (node.kind === 'let' || node.kind === 'var')) {
|
|
216
|
+
return 'variable';
|
|
309
217
|
}
|
|
310
218
|
|
|
311
|
-
//
|
|
312
|
-
if (
|
|
313
|
-
|
|
219
|
+
// for 循环中的变量
|
|
220
|
+
if (node.type === 'ForStatement' && node.init && node.init.type === 'VariableDeclaration' &&
|
|
221
|
+
(node.init.kind === 'let' || node.init.kind === 'var')) {
|
|
222
|
+
return 'variable';
|
|
314
223
|
}
|
|
315
224
|
|
|
316
|
-
//
|
|
317
|
-
if (
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
322
|
-
public_function_name,
|
|
323
|
-
"function-name",
|
|
324
|
-
);
|
|
325
|
-
var recommended_name =
|
|
326
|
-
smart_abbreviation && smart_abbreviation !== public_function_name
|
|
327
|
-
? smart_abbreviation
|
|
328
|
-
: public_function_name;
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
node: node,
|
|
332
|
-
message:
|
|
333
|
-
"函数名'" +
|
|
334
|
-
function_name +
|
|
335
|
-
"'不允许声明私有函数,建议使用: " +
|
|
336
|
-
recommended_name,
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// 检查命名风格
|
|
341
|
-
var style_err = this._validate_naming_style(
|
|
342
|
-
function_name,
|
|
343
|
-
rule.styles,
|
|
344
|
-
"function-name",
|
|
345
|
-
);
|
|
346
|
-
if (style_err) {
|
|
347
|
-
// 使用智能缩写生成推荐名称
|
|
348
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
349
|
-
function_name,
|
|
350
|
-
"function-name",
|
|
351
|
-
);
|
|
352
|
-
var error_message = style_err;
|
|
353
|
-
if (smart_abbreviation && smart_abbreviation !== function_name) {
|
|
354
|
-
error_message = style_err + ",建议使用: " + smart_abbreviation;
|
|
225
|
+
// 对象属性变量 - 先排除函数类型和常量属性
|
|
226
|
+
if ((node.type === 'Property' || node.type === 'ObjectProperty')) {
|
|
227
|
+
// 如果是函数类型,不应该识别为变量
|
|
228
|
+
if (node.value && (node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
229
|
+
return null;
|
|
355
230
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// 检查长度
|
|
363
|
-
var len_err = this._checkNameLength(
|
|
364
|
-
function_name,
|
|
365
|
-
rule.min,
|
|
366
|
-
rule.max,
|
|
367
|
-
"function-name",
|
|
368
|
-
);
|
|
369
|
-
if (len_err) {
|
|
370
|
-
// 使用智能缩写生成推荐名称
|
|
371
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
372
|
-
function_name,
|
|
373
|
-
"function-name",
|
|
374
|
-
);
|
|
375
|
-
var error_message = len_err;
|
|
376
|
-
if (smart_abbreviation && smart_abbreviation !== function_name) {
|
|
377
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
231
|
+
// 属性简写(如 { user_name })应该识别为变量
|
|
232
|
+
if (node.shorthand && node.value && node.value.type === 'Identifier') {
|
|
233
|
+
return 'property-variable';
|
|
378
234
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
// 检查单词长度
|
|
386
|
-
var word_err = this._checkWordLength(
|
|
387
|
-
function_name,
|
|
388
|
-
rule.single_word_len,
|
|
389
|
-
"function-name",
|
|
390
|
-
);
|
|
391
|
-
if (word_err) {
|
|
392
|
-
// 检查错误消息是否已经包含建议
|
|
393
|
-
var error_message = word_err;
|
|
394
|
-
if (!word_err.includes("建议使用")) {
|
|
395
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
396
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
397
|
-
function_name,
|
|
398
|
-
"function-name",
|
|
399
|
-
);
|
|
400
|
-
if (smart_abbreviation && smart_abbreviation !== function_name) {
|
|
401
|
-
error_message = word_err + ",建议使用: " + smart_abbreviation;
|
|
235
|
+
// 只有字面量值且不是常量属性才识别为变量
|
|
236
|
+
if (this._isLiteralValue(node.value)) {
|
|
237
|
+
// 检查是否为常量属性(父级是const声明)
|
|
238
|
+
var parent_type = this._getParentDeclarationType(node);
|
|
239
|
+
if (parent_type !== 'const') {
|
|
240
|
+
return 'property-variable';
|
|
402
241
|
}
|
|
403
242
|
}
|
|
404
|
-
return {
|
|
405
|
-
node: node,
|
|
406
|
-
message: error_message,
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// 检查禁止词汇
|
|
411
|
-
var forbid_err = this._checkForbiddenWords(function_name, "function-name");
|
|
412
|
-
if (forbid_err) {
|
|
413
|
-
// 使用智能缩写生成推荐名称
|
|
414
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
415
|
-
function_name,
|
|
416
|
-
"function-name",
|
|
417
|
-
);
|
|
418
|
-
var error_message = forbid_err;
|
|
419
|
-
if (smart_abbreviation && smart_abbreviation !== function_name) {
|
|
420
|
-
error_message = forbid_err + ",建议使用: " + smart_abbreviation;
|
|
421
|
-
}
|
|
422
|
-
return {
|
|
423
|
-
node: node,
|
|
424
|
-
message: error_message,
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// 检查并推荐更合适的词汇(作为命名风格检查的补充)
|
|
429
|
-
var recommend_info = this._checkAndRecommend(
|
|
430
|
-
function_name,
|
|
431
|
-
"function-name",
|
|
432
|
-
);
|
|
433
|
-
if (recommend_info) {
|
|
434
|
-
// 如果有推荐建议,提示可以使用更简洁的词汇
|
|
435
|
-
return {
|
|
436
|
-
node: node,
|
|
437
|
-
message:
|
|
438
|
-
"函数名'" +
|
|
439
|
-
function_name +
|
|
440
|
-
"'建议使用: " +
|
|
441
|
-
recommend_info.recommended_name,
|
|
442
|
-
};
|
|
443
243
|
}
|
|
444
244
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return null;
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* 检测变量名
|
|
454
|
-
* @param {string} variable_name 变量名
|
|
455
|
-
* @param {object} node AST节点
|
|
456
|
-
* @returns {object|null} 错误信息或null
|
|
457
|
-
*/
|
|
458
|
-
Detector.prototype._checkVariableName = function (variable_name, node) {
|
|
459
|
-
try {
|
|
460
|
-
var rule = this.config.getRule("variable-name");
|
|
461
|
-
if (!rule) {
|
|
462
|
-
return null;
|
|
245
|
+
// 类属性变量
|
|
246
|
+
if (node.type === 'PropertyDefinition' && this._isLiteralValue(node.value)) {
|
|
247
|
+
return node.static ? 'static-variable' : 'property-variable';
|
|
463
248
|
}
|
|
464
249
|
|
|
465
|
-
//
|
|
466
|
-
if (this.
|
|
467
|
-
return
|
|
250
|
+
// 赋值表达式
|
|
251
|
+
if (node.type === 'AssignmentExpression' && this._isLiteralValue(node.right)) {
|
|
252
|
+
return 'property-variable';
|
|
468
253
|
}
|
|
469
254
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
variable_name,
|
|
473
|
-
rule.styles,
|
|
474
|
-
"variable-name",
|
|
475
|
-
);
|
|
476
|
-
if (style_err) {
|
|
477
|
-
return {
|
|
478
|
-
node: node,
|
|
479
|
-
message: style_err,
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// 检查长度
|
|
484
|
-
var len_err = this._checkNameLength(
|
|
485
|
-
variable_name,
|
|
486
|
-
rule.min,
|
|
487
|
-
rule.max,
|
|
488
|
-
"variable-name",
|
|
489
|
-
);
|
|
490
|
-
if (len_err) {
|
|
491
|
-
// 检查错误消息是否已经包含建议
|
|
492
|
-
var error_message = len_err;
|
|
493
|
-
if (!len_err.includes("建议使用")) {
|
|
494
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
495
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
496
|
-
variable_name,
|
|
497
|
-
"variable-name",
|
|
498
|
-
);
|
|
499
|
-
if (smart_abbreviation && smart_abbreviation !== variable_name) {
|
|
500
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
503
257
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
521
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
522
|
-
variable_name,
|
|
523
|
-
"variable-name",
|
|
524
|
-
);
|
|
525
|
-
if (smart_abbreviation && smart_abbreviation !== variable_name) {
|
|
526
|
-
error_message = word_err + ",建议使用: " + smart_abbreviation;
|
|
258
|
+
/**
|
|
259
|
+
* 检测声明方式问题
|
|
260
|
+
* @param {object} node AST节点
|
|
261
|
+
* @returns {string} 问题类型
|
|
262
|
+
*/
|
|
263
|
+
detectDeclarationIssue(node) {
|
|
264
|
+
// const 声明但包含非基础字面量 - 应该用let而不是const
|
|
265
|
+
if (node.type === 'VariableDeclaration' && node.kind === 'const') {
|
|
266
|
+
if (node.declarations && node.declarations.length > 0) {
|
|
267
|
+
var decl = node.declarations[0];
|
|
268
|
+
if (decl.init && !this._isBasicLiteralValue(decl.init)) {
|
|
269
|
+
// 排除函数表达式和箭头函数表达式,它们应该使用const声明
|
|
270
|
+
if (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression') {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return 'const-should-be-let';
|
|
527
274
|
}
|
|
528
275
|
}
|
|
529
|
-
return {
|
|
530
|
-
node: node,
|
|
531
|
-
message: error_message,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// 检查禁止词汇
|
|
536
|
-
var forbid_err = this._checkForbiddenWords(variable_name, "variable-name");
|
|
537
|
-
if (forbid_err) {
|
|
538
|
-
return {
|
|
539
|
-
node: node,
|
|
540
|
-
message: forbid_err,
|
|
541
|
-
};
|
|
542
276
|
}
|
|
543
277
|
|
|
544
|
-
return null;
|
|
545
|
-
} catch (error) {
|
|
546
|
-
console.error("检测变量名时出错:", error);
|
|
547
278
|
return null;
|
|
548
279
|
}
|
|
549
|
-
};
|
|
550
280
|
|
|
551
|
-
/**
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
281
|
+
/**
|
|
282
|
+
* 检测常量类型
|
|
283
|
+
* @param {object} node AST节点
|
|
284
|
+
* @returns {string} 类型名称
|
|
285
|
+
*/
|
|
286
|
+
detectConstantType(node) {
|
|
287
|
+
// const 声明 - 当右侧是字面量值(包括对象和数组)时视为常量
|
|
288
|
+
if (node.type === 'VariableDeclaration' && node.kind === 'const') {
|
|
289
|
+
// 检查右侧是否是字面量值
|
|
290
|
+
if (node.declarations && node.declarations.length > 0) {
|
|
291
|
+
var decl = node.declarations[0];
|
|
292
|
+
if (decl.init && this._isLiteralValue(decl.init)) {
|
|
293
|
+
return 'constant';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
562
296
|
}
|
|
563
297
|
|
|
564
|
-
//
|
|
565
|
-
if (
|
|
566
|
-
return null;
|
|
298
|
+
// var 和 let 声明应该被识别为变量,而不是常量
|
|
299
|
+
if (node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let')) {
|
|
300
|
+
return null; // 返回null,让其他检测器处理
|
|
567
301
|
}
|
|
568
302
|
|
|
569
|
-
//
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
// 使用智能缩写生成推荐名称
|
|
577
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
578
|
-
param_name,
|
|
579
|
-
"param-name",
|
|
580
|
-
);
|
|
581
|
-
var error_message = style_err;
|
|
582
|
-
if (smart_abbreviation && smart_abbreviation !== param_name) {
|
|
583
|
-
error_message = style_err + ",建议使用: " + smart_abbreviation;
|
|
584
|
-
}
|
|
585
|
-
return {
|
|
586
|
-
node: node,
|
|
587
|
-
message: error_message,
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// 检查长度
|
|
592
|
-
var len_err = this._checkNameLength(
|
|
593
|
-
param_name,
|
|
594
|
-
rule.min,
|
|
595
|
-
rule.max,
|
|
596
|
-
"param-name",
|
|
597
|
-
);
|
|
598
|
-
if (len_err) {
|
|
599
|
-
// 检查错误消息是否已经包含建议
|
|
600
|
-
var error_message = len_err;
|
|
601
|
-
if (!len_err.includes("建议使用")) {
|
|
602
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
603
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
604
|
-
param_name,
|
|
605
|
-
"param-name",
|
|
606
|
-
);
|
|
607
|
-
if (smart_abbreviation && smart_abbreviation !== param_name) {
|
|
608
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
303
|
+
// 对象属性常量 - 只有当父级是const声明且属性值是字面量时才视为常量
|
|
304
|
+
if ((node.type === 'Property' || node.type === 'ObjectProperty')) {
|
|
305
|
+
// 只有当值是字面量时才视为常量
|
|
306
|
+
if (this._isLiteralValue(node.value)) {
|
|
307
|
+
var parent_type = this._getParentDeclarationType(node);
|
|
308
|
+
if (parent_type === 'const') {
|
|
309
|
+
return 'property-constant';
|
|
609
310
|
}
|
|
610
311
|
}
|
|
312
|
+
}
|
|
611
313
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
// 检查单词长度
|
|
619
|
-
var word_err = this._checkWordLength(
|
|
620
|
-
param_name,
|
|
621
|
-
rule.single_word_len,
|
|
622
|
-
"param-name",
|
|
623
|
-
);
|
|
624
|
-
if (word_err) {
|
|
625
|
-
// 检查错误消息是否已经包含建议
|
|
626
|
-
var error_message = word_err;
|
|
627
|
-
if (!word_err.includes("建议使用")) {
|
|
628
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
629
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
630
|
-
param_name,
|
|
631
|
-
"param-name",
|
|
632
|
-
);
|
|
633
|
-
if (smart_abbreviation && smart_abbreviation !== param_name) {
|
|
634
|
-
error_message = word_err + ",建议使用: " + smart_abbreviation;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
return {
|
|
638
|
-
node: node,
|
|
639
|
-
message: error_message,
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// 检查禁止词汇
|
|
644
|
-
var forbid_err = this._checkForbiddenWords(param_name, "param-name");
|
|
645
|
-
if (forbid_err) {
|
|
646
|
-
// 使用智能缩写生成推荐名称
|
|
647
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
648
|
-
param_name,
|
|
649
|
-
"param-name",
|
|
650
|
-
);
|
|
651
|
-
var error_message = forbid_err;
|
|
652
|
-
if (smart_abbreviation && smart_abbreviation !== param_name) {
|
|
653
|
-
error_message = forbid_err + ",建议使用: " + smart_abbreviation;
|
|
314
|
+
// 类属性常量 - 只有当属性名符合常量命名规范时才识别为常量
|
|
315
|
+
if (node.type === 'PropertyDefinition' && this._isLiteralValue(node.value)) {
|
|
316
|
+
if (node.static) {
|
|
317
|
+
return 'static-variable';
|
|
654
318
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
message: error_message,
|
|
658
|
-
};
|
|
319
|
+
// 类中的静态属性应该被识别为静态属性-变量,而不是静态属性-常量
|
|
320
|
+
return 'variable';
|
|
659
321
|
}
|
|
660
322
|
|
|
661
|
-
return null;
|
|
662
|
-
} catch (error) {
|
|
663
|
-
console.error("检测参数名时出错:", error);
|
|
664
323
|
return null;
|
|
665
324
|
}
|
|
666
|
-
};
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* 检测常量名
|
|
670
|
-
* @param {string} constant_name 常量名
|
|
671
|
-
* @param {object} node AST节点
|
|
672
|
-
* @param {object} val_node 值节点(用于识别基础值)
|
|
673
|
-
* @returns {object|null} 错误信息或null
|
|
674
|
-
*/
|
|
675
|
-
Detector.prototype._checkConstantName = function (
|
|
676
|
-
constant_name,
|
|
677
|
-
node,
|
|
678
|
-
val_node,
|
|
679
|
-
) {
|
|
680
|
-
try {
|
|
681
|
-
// 检查是否为解构导入(从模块导入的变量)
|
|
682
|
-
var is_destructured_import = this._isDestructuredImport(node);
|
|
683
|
-
|
|
684
|
-
// 如果是解构导入,则不进行命名检测
|
|
685
|
-
if (is_destructured_import) {
|
|
686
|
-
return null;
|
|
687
|
-
}
|
|
688
325
|
|
|
689
|
-
|
|
690
|
-
|
|
326
|
+
/**
|
|
327
|
+
* 检测参数类型
|
|
328
|
+
* @param {object} node AST节点
|
|
329
|
+
* @returns {string} 类型名称
|
|
330
|
+
*/
|
|
331
|
+
detectParamType(node) {
|
|
332
|
+
// 只在真正的函数参数节点上返回参数类型
|
|
333
|
+
if (node.type === 'Identifier' && node.parent &&
|
|
334
|
+
(node.parent.type === 'FunctionDeclaration' ||
|
|
335
|
+
node.parent.type === 'FunctionExpression' ||
|
|
336
|
+
node.parent.type === 'ArrowFunctionExpression') &&
|
|
337
|
+
node.parent.params && node.parent.params.includes(node)) {
|
|
338
|
+
|
|
339
|
+
// 增强版参数类型检测:根据使用上下文推断参数类型
|
|
340
|
+
var param_type = this._inferParamTypeFromContext(node);
|
|
341
|
+
|
|
342
|
+
// 如果使用上下文无法推断,尝试其他方法
|
|
343
|
+
if (!param_type) {
|
|
344
|
+
// 基于命名约定推断(优先级高于命名风格)
|
|
345
|
+
param_type = this._inferParamTypeByNamingConvention(node.name);
|
|
346
|
+
|
|
347
|
+
// 如果命名约定也无法推断,基于命名风格推断
|
|
348
|
+
if (!param_type) {
|
|
349
|
+
param_type = this._inferParamTypeByStyle(node.name);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
691
352
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
};
|
|
353
|
+
// 对于箭头函数参数,优先推断为变量类型
|
|
354
|
+
if (node.parent && node.parent.type === 'ArrowFunctionExpression') {
|
|
355
|
+
param_type = 'variable';
|
|
356
|
+
}
|
|
357
|
+
return param_type ? 'param-' + param_type : 'param';
|
|
698
358
|
}
|
|
699
359
|
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
|
|
360
|
+
// 不是参数节点,返回null让其他检测器处理
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
703
363
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
// 检查是否为类实例(new表达式)
|
|
717
|
-
else if (val_node && val_node.type === "NewExpression") {
|
|
718
|
-
rule_type = "function-name"; // 类实例使用函数命名规则(camelCase)
|
|
719
|
-
}
|
|
720
|
-
// 检查是否为基础值类型
|
|
721
|
-
else if (val_node && this._isBaseValue(val_node)) {
|
|
722
|
-
rule_type = "constant-name";
|
|
723
|
-
}
|
|
724
|
-
// 检查是否为其他复杂类型(函数调用、变量引用等)
|
|
725
|
-
else if (val_node && this._isComplexType(val_node)) {
|
|
726
|
-
// 其他复杂类型不进行命名检测
|
|
727
|
-
return null;
|
|
728
|
-
}
|
|
729
|
-
// 其他情况不进行命名检测
|
|
730
|
-
else {
|
|
731
|
-
return null;
|
|
364
|
+
/**
|
|
365
|
+
* 根据上下文推断参数类型
|
|
366
|
+
* @param {object} param_node 参数节点
|
|
367
|
+
* @returns {string} 参数类型(function/class/variable)
|
|
368
|
+
*/
|
|
369
|
+
_inferParamTypeFromContext(param_node) {
|
|
370
|
+
var function_node = param_node.parent;
|
|
371
|
+
var param_name = param_node.name;
|
|
372
|
+
|
|
373
|
+
// 如果没有函数体,无法进行使用上下文推断
|
|
374
|
+
if (!function_node.body) {
|
|
375
|
+
return null; // 无法推断,返回null让上层逻辑处理
|
|
732
376
|
}
|
|
733
377
|
|
|
734
|
-
//
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
//
|
|
739
|
-
if (
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
else if (rule_type === 'property-instance-class-name') {
|
|
744
|
-
rule_config_type = 'variable-name';
|
|
745
|
-
}
|
|
746
|
-
// 属性方法名 -> 方法名
|
|
747
|
-
else if (rule_type === 'property-method-name') {
|
|
748
|
-
rule_config_type = 'method-name';
|
|
378
|
+
// 收集参数在函数体中的所有使用方式
|
|
379
|
+
var usage_types = [];
|
|
380
|
+
|
|
381
|
+
this._traverseFunctionBody(function_node.body, function (node) {
|
|
382
|
+
// 检查调用表达式:param_name() → 函数类型
|
|
383
|
+
if (node.type === 'CallExpression' &&
|
|
384
|
+
node.callee.type === 'Identifier' &&
|
|
385
|
+
node.callee.name === param_name) {
|
|
386
|
+
usage_types.push('function_call');
|
|
749
387
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
388
|
+
|
|
389
|
+
// 检查new表达式:new param_name() → 类类型
|
|
390
|
+
else if (node.type === 'NewExpression' &&
|
|
391
|
+
node.callee.type === 'Identifier' &&
|
|
392
|
+
node.callee.name === param_name) {
|
|
393
|
+
usage_types.push('class_instantiation');
|
|
753
394
|
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
var rule = this.config.getRule(rule_config_type);
|
|
757
|
-
if (!rule) {
|
|
758
|
-
return null;
|
|
759
|
-
}
|
|
760
395
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
396
|
+
// 检查成员表达式:param_name.property → 对象类型
|
|
397
|
+
else if (node.type === 'MemberExpression' &&
|
|
398
|
+
node.object.type === 'Identifier' &&
|
|
399
|
+
node.object.name === param_name) {
|
|
400
|
+
usage_types.push('property_access');
|
|
401
|
+
}
|
|
765
402
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
if (style_err) {
|
|
773
|
-
// 使用智能缩写生成推荐名称
|
|
774
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
775
|
-
constant_name,
|
|
776
|
-
rule_type,
|
|
777
|
-
);
|
|
778
|
-
var error_message = style_err;
|
|
779
|
-
if (smart_abbreviation && smart_abbreviation !== constant_name) {
|
|
780
|
-
error_message = style_err + ",建议使用: " + smart_abbreviation;
|
|
403
|
+
// 检查方法调用:param_name.method() → 对象类型(类实例)
|
|
404
|
+
else if (node.type === 'CallExpression' &&
|
|
405
|
+
node.callee.type === 'MemberExpression' &&
|
|
406
|
+
node.callee.object.type === 'Identifier' &&
|
|
407
|
+
node.callee.object.name === param_name) {
|
|
408
|
+
usage_types.push('method_call');
|
|
781
409
|
}
|
|
782
410
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// 检查长度
|
|
791
|
-
var len_err = this._checkNameLength(
|
|
792
|
-
constant_name,
|
|
793
|
-
rule.min,
|
|
794
|
-
rule.max,
|
|
795
|
-
rule_type,
|
|
796
|
-
);
|
|
797
|
-
if (len_err) {
|
|
798
|
-
// 使用智能缩写生成推荐名称
|
|
799
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
800
|
-
constant_name,
|
|
801
|
-
rule_type,
|
|
802
|
-
);
|
|
803
|
-
var error_message = len_err;
|
|
804
|
-
if (smart_abbreviation && smart_abbreviation !== constant_name) {
|
|
805
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
411
|
+
// 检查赋值表达式:var x = param_name → 变量类型
|
|
412
|
+
else if (node.type === 'VariableDeclarator' &&
|
|
413
|
+
node.init && node.init.type === 'Identifier' &&
|
|
414
|
+
node.init.name === param_name) {
|
|
415
|
+
usage_types.push('variable_assignment');
|
|
806
416
|
}
|
|
807
417
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
var word_err = this._checkWordLength(
|
|
817
|
-
constant_name,
|
|
818
|
-
rule.single_word_len,
|
|
819
|
-
rule_type,
|
|
820
|
-
);
|
|
821
|
-
if (word_err) {
|
|
822
|
-
return {
|
|
823
|
-
node: node,
|
|
824
|
-
message: word_err,
|
|
825
|
-
rule_type: rule_type, // 返回正确的规则类型
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// 检查禁止词汇
|
|
830
|
-
var forbid_err = this._checkForbiddenWords(constant_name, rule_type);
|
|
831
|
-
if (forbid_err) {
|
|
832
|
-
return {
|
|
833
|
-
node: node,
|
|
834
|
-
message: forbid_err,
|
|
835
|
-
rule_type: rule_type, // 返回正确的规则类型
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// 检查并推荐更合适的词汇(作为命名风格检查的补充)
|
|
840
|
-
var recommend_info = this._checkAndRecommend(constant_name, rule_type);
|
|
841
|
-
if (recommend_info) {
|
|
842
|
-
// 根据规则类型生成正确的错误消息
|
|
843
|
-
var name_type_text = "";
|
|
844
|
-
switch (rule_type) {
|
|
845
|
-
case "function-name":
|
|
846
|
-
name_type_text = "函数名";
|
|
847
|
-
break;
|
|
848
|
-
case "class-name":
|
|
849
|
-
name_type_text = "类名";
|
|
850
|
-
break;
|
|
851
|
-
case "constant-name":
|
|
852
|
-
name_type_text = "常量名";
|
|
853
|
-
break;
|
|
854
|
-
case "variable-name":
|
|
855
|
-
name_type_text = "变量名";
|
|
856
|
-
break;
|
|
857
|
-
default:
|
|
858
|
-
name_type_text = "名称";
|
|
418
|
+
// 检查作为参数传递:func(param_name) → 根据接收函数的上下文推断
|
|
419
|
+
else if (node.type === 'CallExpression') {
|
|
420
|
+
for (var i = 0; i < node.arguments.length; i++) {
|
|
421
|
+
var arg = node.arguments[i];
|
|
422
|
+
if (arg.type === 'Identifier' && arg.name === param_name) {
|
|
423
|
+
usage_types.push('function_argument');
|
|
424
|
+
}
|
|
425
|
+
}
|
|
859
426
|
}
|
|
860
427
|
|
|
861
|
-
return
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
428
|
+
// 检查直接使用:return name, var x = name → 变量类型
|
|
429
|
+
else if (node.type === 'Identifier' && node.name === param_name) {
|
|
430
|
+
// 排除已经在其他检测中处理的情况
|
|
431
|
+
var parent = node.parent;
|
|
432
|
+
if (!parent ||
|
|
433
|
+
(parent.type !== 'CallExpression' &&
|
|
434
|
+
parent.type !== 'NewExpression' &&
|
|
435
|
+
parent.type !== 'MemberExpression' &&
|
|
436
|
+
!(parent.type === 'VariableDeclarator' && parent.init === node))) {
|
|
437
|
+
usage_types.push('direct_usage');
|
|
438
|
+
}
|
|
439
|
+
}
|
|
872
440
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
console.error("检测常量名时出错:", error);
|
|
876
|
-
return null;
|
|
877
|
-
}
|
|
878
|
-
};
|
|
441
|
+
return false; // 继续遍历,收集所有使用方式
|
|
442
|
+
});
|
|
879
443
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
* @param {string} name 名称
|
|
883
|
-
* @param {Array} styles 允许的风格列表
|
|
884
|
-
* @param {string} name_type 名称类型
|
|
885
|
-
* @returns {string|null} 错误信息或null
|
|
886
|
-
*/
|
|
887
|
-
Detector.prototype._validate_naming_style = function (name, styles, name_type) {
|
|
888
|
-
if (!name || !styles || !Array.isArray(styles)) {
|
|
889
|
-
return null;
|
|
444
|
+
// 根据使用方式推断参数类型
|
|
445
|
+
return this._inferTypeFromUsage(usage_types, param_name);
|
|
890
446
|
}
|
|
891
447
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
448
|
+
/**
|
|
449
|
+
* 根据使用方式推断参数类型
|
|
450
|
+
* @param {Array} usage_types 使用方式数组
|
|
451
|
+
* @param {string} param_name 参数名
|
|
452
|
+
* @returns {string} 推断的参数类型
|
|
453
|
+
*/
|
|
454
|
+
_inferTypeFromUsage(usage_types, param_name) {
|
|
455
|
+
// 如果没有使用方式,无法推断
|
|
456
|
+
if (usage_types.length === 0) {
|
|
896
457
|
return null;
|
|
897
458
|
}
|
|
898
|
-
}
|
|
899
459
|
|
|
900
|
-
|
|
460
|
+
// 优先级:函数调用 > 类实例化 > 方法调用 > 属性访问 > 函数参数 > 变量赋值 > 直接使用
|
|
461
|
+
var priority_map = {
|
|
462
|
+
'function_call': 7, // 最高优先级:明确是函数
|
|
463
|
+
'class_instantiation': 6, // 高优先级:明确是类
|
|
464
|
+
'method_call': 5, // 中高优先级:方法调用(类实例)
|
|
465
|
+
'property_access': 4, // 中优先级:对象属性访问
|
|
466
|
+
'function_argument': 3, // 低优先级:作为函数参数传递(根据接收函数推断)
|
|
467
|
+
'variable_assignment': 2, // 较低优先级:变量赋值
|
|
468
|
+
'direct_usage': 1 // 最低优先级:直接使用
|
|
469
|
+
};
|
|
901
470
|
|
|
902
|
-
|
|
471
|
+
// 找到最高优先级的用法
|
|
472
|
+
var highest_priority = 0;
|
|
473
|
+
var inferred_type = null;
|
|
903
474
|
|
|
904
|
-
|
|
905
|
-
|
|
475
|
+
for (var i = 0; i < usage_types.length; i++) {
|
|
476
|
+
var usage = usage_types[i];
|
|
477
|
+
var priority = priority_map[usage] || 0;
|
|
906
478
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
* @param {string} name 名称
|
|
910
|
-
* @param {number} min 最小长度
|
|
911
|
-
* @param {number} max 最大长度
|
|
912
|
-
* @returns {string|null} 错误信息或null
|
|
913
|
-
*/
|
|
914
|
-
Detector.prototype._checkNameLength = function (name, min, max, name_type) {
|
|
915
|
-
if (!name) {
|
|
916
|
-
return "名称不能为空";
|
|
917
|
-
}
|
|
479
|
+
if (priority > highest_priority) {
|
|
480
|
+
highest_priority = priority;
|
|
918
481
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
482
|
+
// 根据使用方式映射到参数类型
|
|
483
|
+
switch (usage) {
|
|
484
|
+
case 'function_call':
|
|
485
|
+
inferred_type = 'function';
|
|
486
|
+
break;
|
|
487
|
+
case 'class_instantiation':
|
|
488
|
+
inferred_type = 'class';
|
|
489
|
+
break;
|
|
490
|
+
case 'method_call':
|
|
491
|
+
inferred_type = 'class-instance';
|
|
492
|
+
break;
|
|
493
|
+
case 'property_access':
|
|
494
|
+
inferred_type = 'object';
|
|
495
|
+
break;
|
|
496
|
+
case 'function_argument':
|
|
497
|
+
// 作为函数参数传递,需要根据接收函数的上下文推断
|
|
498
|
+
inferred_type = 'variable'; // 默认变量类型,让其他使用方式覆盖
|
|
499
|
+
break;
|
|
500
|
+
case 'variable_assignment':
|
|
501
|
+
case 'direct_usage':
|
|
502
|
+
inferred_type = 'variable';
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
924
507
|
|
|
925
|
-
|
|
926
|
-
return name_type_desc + "'" + name + "'长度过长,最大长度为" + max + "字符";
|
|
508
|
+
return inferred_type;
|
|
927
509
|
}
|
|
928
510
|
|
|
929
|
-
|
|
930
|
-
|
|
511
|
+
/**
|
|
512
|
+
* 基于命名约定推断参数类型
|
|
513
|
+
* @param {string} param_name 参数名
|
|
514
|
+
* @returns {string} 推断的参数类型
|
|
515
|
+
*/
|
|
516
|
+
_inferParamTypeByNamingConvention(param_name) {
|
|
517
|
+
var lower_name = param_name.toLowerCase();
|
|
518
|
+
|
|
519
|
+
// 函数类型命名约定
|
|
520
|
+
if (lower_name.startsWith('on') ||
|
|
521
|
+
lower_name.startsWith('handle') ||
|
|
522
|
+
lower_name.endsWith('func') ||
|
|
523
|
+
lower_name.endsWith('callback') ||
|
|
524
|
+
lower_name.endsWith('handler')) {
|
|
525
|
+
return 'function';
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// 类类型命名约定
|
|
529
|
+
if (lower_name.endsWith('manager') ||
|
|
530
|
+
lower_name.endsWith('service') ||
|
|
531
|
+
lower_name.endsWith('controller') ||
|
|
532
|
+
lower_name.endsWith('provider') ||
|
|
533
|
+
lower_name.endsWith('factory') ||
|
|
534
|
+
lower_name.endsWith('class') ||
|
|
535
|
+
lower_name.endsWith('model') ||
|
|
536
|
+
lower_name.endsWith('view') ||
|
|
537
|
+
lower_name.endsWith('component')) {
|
|
538
|
+
return 'class';
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// 对象类型命名约定
|
|
542
|
+
if (lower_name.endsWith('obj') ||
|
|
543
|
+
lower_name.endsWith('object') ||
|
|
544
|
+
lower_name.endsWith('data') ||
|
|
545
|
+
lower_name.endsWith('info') ||
|
|
546
|
+
lower_name.endsWith('config') ||
|
|
547
|
+
lower_name.endsWith('options')) {
|
|
548
|
+
return 'variable';
|
|
549
|
+
}
|
|
931
550
|
|
|
932
|
-
/**
|
|
933
|
-
* 检查单词长度
|
|
934
|
-
* @param {string} name 名称
|
|
935
|
-
* @param {number} max_word_len 最大单词长度
|
|
936
|
-
* @returns {string|null} 错误信息或null
|
|
937
|
-
*/
|
|
938
|
-
Detector.prototype._checkWordLength = function (name, max_word_len, name_type) {
|
|
939
|
-
if (!name || !max_word_len) {
|
|
940
551
|
return null;
|
|
941
552
|
}
|
|
942
553
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
554
|
+
/**
|
|
555
|
+
* 基于命名风格推断参数类型
|
|
556
|
+
* @param {string} param_name 参数名
|
|
557
|
+
* @returns {string} 推断的参数类型
|
|
558
|
+
*/
|
|
559
|
+
_inferParamTypeByStyle(param_name) {
|
|
560
|
+
var config = this.config || new (require('./config').Config)();
|
|
948
561
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
562
|
+
// 检查是否符合PascalCase(类类型)
|
|
563
|
+
if (config.getRegex('PascalCase').test(param_name)) {
|
|
564
|
+
return 'class';
|
|
565
|
+
}
|
|
953
566
|
|
|
954
|
-
|
|
955
|
-
|
|
567
|
+
// 检查是否符合snake_case(变量/对象类型)
|
|
568
|
+
if (config.getRegex('snake_case').test(param_name)) {
|
|
569
|
+
return 'variable';
|
|
570
|
+
}
|
|
956
571
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
} else {
|
|
962
|
-
// 驼峰命名法分割(保持原词大小写)
|
|
963
|
-
words = processed_name.split(/(?=[A-Z])/);
|
|
964
|
-
}
|
|
572
|
+
// 检查是否符合camelCase(函数类型)
|
|
573
|
+
if (config.getRegex('camelCase').test(param_name)) {
|
|
574
|
+
return 'function';
|
|
575
|
+
}
|
|
965
576
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
words.unshift("_");
|
|
577
|
+
// 默认变量类型
|
|
578
|
+
return 'variable';
|
|
969
579
|
}
|
|
970
580
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
581
|
+
/**
|
|
582
|
+
* 语义推断参数类型
|
|
583
|
+
* @param {object} param_node 参数节点
|
|
584
|
+
* @returns {string} 推断的参数类型
|
|
585
|
+
*/
|
|
586
|
+
_inferParamTypeSemantically(param_node) {
|
|
587
|
+
var function_node = param_node.parent;
|
|
588
|
+
var param_name = param_node.name;
|
|
589
|
+
|
|
590
|
+
// 收集函数体中所有标识符
|
|
591
|
+
var identifiers = [];
|
|
592
|
+
this._traverseFunctionBody(function_node.body, function (node) {
|
|
593
|
+
if (node.type === 'Identifier') {
|
|
594
|
+
identifiers.push({
|
|
595
|
+
name: node.name,
|
|
596
|
+
type: this._getIdentifierUsageType(node),
|
|
597
|
+
node: node
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
return false;
|
|
601
|
+
}.bind(this));
|
|
602
|
+
|
|
603
|
+
// 分析标识符与参数的语义关联
|
|
604
|
+
for (var i = 0; i < identifiers.length; i++) {
|
|
605
|
+
var identifier = identifiers[i];
|
|
606
|
+
|
|
607
|
+
// 检查标识符是否与参数名称有语义关联
|
|
608
|
+
if (this._hasSemanticRelation(param_name, identifier.name)) {
|
|
609
|
+
// 根据标识符的使用类型推断参数类型
|
|
610
|
+
switch (identifier.type) {
|
|
611
|
+
case 'function_call':
|
|
612
|
+
return 'function';
|
|
613
|
+
case 'class_instantiation':
|
|
614
|
+
return 'class';
|
|
615
|
+
case 'property_access':
|
|
616
|
+
return 'class-instance';
|
|
617
|
+
case 'variable_declaration':
|
|
618
|
+
return 'variable';
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
975
622
|
|
|
976
|
-
// 如果是单个单词,则不检测单词长度
|
|
977
|
-
if (words.length <= 1) {
|
|
978
623
|
return null;
|
|
979
624
|
}
|
|
980
625
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
626
|
+
/**
|
|
627
|
+
* 检查两个标识符是否有语义关联
|
|
628
|
+
* @param {string} param_name 参数名
|
|
629
|
+
* @param {string} identifier_name 标识符名
|
|
630
|
+
* @returns {boolean} 是否有语义关联
|
|
631
|
+
*/
|
|
632
|
+
_hasSemanticRelation(param_name, identifier_name) {
|
|
633
|
+
// 简单的语义关联规则
|
|
987
634
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
var recommendation_text = "";
|
|
992
|
-
if (smart_abbreviation && smart_abbreviation !== name) {
|
|
993
|
-
recommendation_text = ",建议使用: " + smart_abbreviation;
|
|
994
|
-
}
|
|
635
|
+
// 1. 名称相似性检查(忽略大小写和下划线)
|
|
636
|
+
var normalized_param = param_name.toLowerCase().replace(/[_-]/g, '');
|
|
637
|
+
var normalized_ident = identifier_name.toLowerCase().replace(/[_-]/g, '');
|
|
995
638
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
"'" +
|
|
999
|
-
name +
|
|
1000
|
-
"'中的单词'" +
|
|
1001
|
-
word +
|
|
1002
|
-
"'过长,最大长度为" +
|
|
1003
|
-
max_word_len +
|
|
1004
|
-
"字符" +
|
|
1005
|
-
recommendation_text
|
|
1006
|
-
);
|
|
639
|
+
if (normalized_param === normalized_ident) {
|
|
640
|
+
return true;
|
|
1007
641
|
}
|
|
1008
|
-
}
|
|
1009
642
|
|
|
1010
|
-
|
|
1011
|
-
|
|
643
|
+
// 2. 包含关系检查
|
|
644
|
+
if (normalized_ident.includes(normalized_param) ||
|
|
645
|
+
normalized_param.includes(normalized_ident)) {
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
1012
648
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
* @param {string} name_type 名称类型
|
|
1017
|
-
* @returns {string|null} 错误信息或null
|
|
1018
|
-
*/
|
|
1019
|
-
Detector.prototype._checkForbiddenWords = function (name, name_type) {
|
|
1020
|
-
if (!name || !name_type) {
|
|
1021
|
-
return null;
|
|
1022
|
-
}
|
|
649
|
+
// 3. 词根相似性检查
|
|
650
|
+
var param_root = this._getWordRoot(normalized_param);
|
|
651
|
+
var ident_root = this._getWordRoot(normalized_ident);
|
|
1023
652
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
653
|
+
if (param_root && ident_root && param_root === ident_root) {
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return false;
|
|
1027
658
|
}
|
|
1028
659
|
|
|
1029
|
-
|
|
1030
|
-
|
|
660
|
+
/**
|
|
661
|
+
* 获取标识符的使用类型
|
|
662
|
+
* @param {object} identifier_node 标识符节点
|
|
663
|
+
* @returns {string} 使用类型
|
|
664
|
+
*/
|
|
665
|
+
_getIdentifierUsageType(identifier_node) {
|
|
666
|
+
var parent = identifier_node.parent;
|
|
1031
667
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
// 移除前缀_后检查是否包含分隔符
|
|
1035
|
-
var name_without_prefix = name.substring(1);
|
|
1036
|
-
if (!name_without_prefix.includes("_") && !name_without_prefix.includes("-") && !/[A-Z]/.test(name_without_prefix)) {
|
|
1037
|
-
return null; // 单个单词的私有变量,跳过检查
|
|
668
|
+
if (parent.type === 'CallExpression' && parent.callee === identifier_node) {
|
|
669
|
+
return 'function_call';
|
|
1038
670
|
}
|
|
1039
|
-
}
|
|
1040
671
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
var forbidden_word = forbidden_words[i].toLowerCase();
|
|
1044
|
-
if (name_lower === forbidden_word) {
|
|
1045
|
-
return null; // 单个单词允许使用
|
|
672
|
+
if (parent.type === 'NewExpression' && parent.callee === identifier_node) {
|
|
673
|
+
return 'class_instantiation';
|
|
1046
674
|
}
|
|
1047
|
-
}
|
|
1048
675
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
// 将拆分后的单词转换为小写进行比较
|
|
1053
|
-
var words_lower = words.map(function(word) {
|
|
1054
|
-
return word.toLowerCase();
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
// 检查每个单词是否在禁止词汇列表中
|
|
1058
|
-
var found_forbidden_words = [];
|
|
1059
|
-
|
|
1060
|
-
// 对于私有变量(以_开头),跳过第一个单词(即_)
|
|
1061
|
-
var start_index = name_lower.startsWith("_") ? 1 : 0;
|
|
1062
|
-
|
|
1063
|
-
for (var i = start_index; i < words_lower.length; i++) {
|
|
1064
|
-
var word = words_lower[i];
|
|
1065
|
-
// 跳过空字符串和单个字符
|
|
1066
|
-
if (!word || word.length <= 1) {
|
|
1067
|
-
continue;
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
for (var j = 0; j < forbidden_words.length; j++) {
|
|
1071
|
-
var forbidden_word = forbidden_words[j].toLowerCase();
|
|
1072
|
-
if (word === forbidden_word) {
|
|
1073
|
-
// 记录找到的禁用词,但不立即返回
|
|
1074
|
-
if (found_forbidden_words.indexOf(forbidden_word) === -1) {
|
|
1075
|
-
found_forbidden_words.push(forbidden_word);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
676
|
+
if (parent.type === 'MemberExpression' && parent.object === identifier_node) {
|
|
677
|
+
return 'property_access';
|
|
1078
678
|
}
|
|
1079
|
-
}
|
|
1080
679
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
var error_message = name_type_desc + "'" + name + "'包含禁止拼接词";
|
|
1084
|
-
|
|
1085
|
-
if (found_forbidden_words.length === 1) {
|
|
1086
|
-
error_message += "'" + found_forbidden_words[0] + "'";
|
|
1087
|
-
} else {
|
|
1088
|
-
// 多个禁用词,按优先级排序(长的优先)
|
|
1089
|
-
found_forbidden_words.sort(function(a, b) {
|
|
1090
|
-
return b.length - a.length; // 长的优先
|
|
1091
|
-
});
|
|
1092
|
-
error_message += ":" + found_forbidden_words.join(", ");
|
|
680
|
+
if (parent.type === 'VariableDeclarator' && parent.id === identifier_node) {
|
|
681
|
+
return 'variable_declaration';
|
|
1093
682
|
}
|
|
1094
|
-
|
|
1095
|
-
return error_message;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
return null;
|
|
1099
|
-
};
|
|
1100
683
|
|
|
1101
|
-
|
|
1102
|
-
* 按单词边界分割名称
|
|
1103
|
-
* @param {string} name 名称
|
|
1104
|
-
* @returns {Array} 单词数组
|
|
1105
|
-
*/
|
|
1106
|
-
Detector.prototype._splitNameIntoWords = function (name) {
|
|
1107
|
-
if (!name) {
|
|
1108
|
-
return [];
|
|
684
|
+
return 'other';
|
|
1109
685
|
}
|
|
1110
686
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
687
|
+
/**
|
|
688
|
+
* 获取单词的词根(简化实现)
|
|
689
|
+
* @param {string} word 单词
|
|
690
|
+
* @returns {string} 词根
|
|
691
|
+
*/
|
|
692
|
+
_getWordRoot(word) {
|
|
693
|
+
// 简单的词根提取规则
|
|
694
|
+
var common_suffixes = ['s', 'es', 'ed', 'ing', 'er', 'or', 'ment', 'tion', 'ity'];
|
|
1114
695
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
696
|
+
for (var i = 0; i < common_suffixes.length; i++) {
|
|
697
|
+
var suffix = common_suffixes[i];
|
|
698
|
+
if (word.endsWith(suffix) && word.length > suffix.length) {
|
|
699
|
+
return word.slice(0, -suffix.length);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
1120
702
|
|
|
1121
|
-
|
|
1122
|
-
if (!processed_name) {
|
|
1123
|
-
return ["_"];
|
|
703
|
+
return word;
|
|
1124
704
|
}
|
|
1125
705
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
words = processed_name.split(/(?=[A-Z])/);
|
|
1134
|
-
}
|
|
706
|
+
/**
|
|
707
|
+
* 遍历函数体
|
|
708
|
+
* @param {object} body_node 函数体节点
|
|
709
|
+
* @param {function} visitor 访问器函数
|
|
710
|
+
*/
|
|
711
|
+
_traverseFunctionBody(body_node, visitor) {
|
|
712
|
+
if (!body_node || !visitor) return;
|
|
1135
713
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
words.unshift("_");
|
|
1139
|
-
}
|
|
714
|
+
// 简单的AST遍历实现
|
|
715
|
+
var nodes_to_visit = [body_node];
|
|
1140
716
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
return word && word.length > 0;
|
|
1144
|
-
});
|
|
717
|
+
while (nodes_to_visit.length > 0) {
|
|
718
|
+
var current_node = nodes_to_visit.pop();
|
|
1145
719
|
|
|
1146
|
-
|
|
1147
|
-
|
|
720
|
+
// 调用访问器,如果返回true则停止遍历
|
|
721
|
+
if (visitor(current_node)) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
1148
724
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
// 处理新的父节点信息结构(包含node和parent属性)
|
|
1158
|
-
var actual_parent_node = parent_node;
|
|
1159
|
-
if (parent_node && typeof parent_node === "object" && parent_node.node) {
|
|
1160
|
-
actual_parent_node = parent_node.node;
|
|
1161
|
-
}
|
|
725
|
+
// 添加子节点到遍历队列
|
|
726
|
+
if (current_node.body) {
|
|
727
|
+
if (Array.isArray(current_node.body)) {
|
|
728
|
+
nodes_to_visit.push(...current_node.body.reverse());
|
|
729
|
+
} else {
|
|
730
|
+
nodes_to_visit.push(current_node.body);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
1162
733
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
}
|
|
734
|
+
if (current_node.expression) {
|
|
735
|
+
nodes_to_visit.push(current_node.expression);
|
|
736
|
+
}
|
|
1167
737
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
if (!current_node) {
|
|
1171
|
-
return null;
|
|
738
|
+
if (current_node.consequent) {
|
|
739
|
+
nodes_to_visit.push(current_node.consequent);
|
|
1172
740
|
}
|
|
1173
741
|
|
|
1174
|
-
if (current_node.
|
|
1175
|
-
|
|
742
|
+
if (current_node.alternate) {
|
|
743
|
+
nodes_to_visit.push(current_node.alternate);
|
|
1176
744
|
}
|
|
1177
745
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
746
|
+
if (current_node.argument) {
|
|
747
|
+
nodes_to_visit.push(current_node.argument);
|
|
748
|
+
}
|
|
1181
749
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
? parent_node.parent
|
|
1186
|
-
: actual_parent_node.parent;
|
|
750
|
+
if (current_node.arguments && Array.isArray(current_node.arguments)) {
|
|
751
|
+
nodes_to_visit.push(...current_node.arguments.reverse());
|
|
752
|
+
}
|
|
1187
753
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
return false;
|
|
1192
|
-
}
|
|
754
|
+
if (current_node.elements && Array.isArray(current_node.elements)) {
|
|
755
|
+
nodes_to_visit.push(...current_node.elements.reverse());
|
|
756
|
+
}
|
|
1193
757
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
return false;
|
|
758
|
+
if (current_node.properties && Array.isArray(current_node.properties)) {
|
|
759
|
+
nodes_to_visit.push(...current_node.properties.reverse());
|
|
760
|
+
}
|
|
1198
761
|
}
|
|
762
|
+
}
|
|
1199
763
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
764
|
+
/**
|
|
765
|
+
* 检测命名类型
|
|
766
|
+
* @param {object} node AST节点
|
|
767
|
+
* @param {string} name 名称
|
|
768
|
+
* @returns {object} 检测结果
|
|
769
|
+
*/
|
|
770
|
+
detect(node, name) {
|
|
771
|
+
var type = null;
|
|
772
|
+
|
|
773
|
+
// 按优先级检测类型 - 变量检测应该在常量检测之前
|
|
774
|
+
var detectors = [
|
|
775
|
+
this.detectClassType.bind(this),
|
|
776
|
+
this.detectClassInstanceType.bind(this),
|
|
777
|
+
this.detectFunctionType.bind(this), // 函数检测应该在变量检测之前
|
|
778
|
+
this.detectConstantType.bind(this), // 常量检测优先级提高
|
|
779
|
+
this.detectVariableType.bind(this), // 变量检测优先级降低
|
|
780
|
+
this.detectParamType.bind(this)
|
|
781
|
+
];
|
|
1207
782
|
|
|
1208
|
-
|
|
1209
|
-
|
|
783
|
+
for (var i = 0; i < detectors.length; i++) {
|
|
784
|
+
type = detectors[i](node);
|
|
785
|
+
if (type) {
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
1210
788
|
}
|
|
1211
789
|
|
|
1212
|
-
//
|
|
1213
|
-
if (
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
"listen",
|
|
1231
|
-
"send",
|
|
1232
|
-
];
|
|
1233
|
-
|
|
1234
|
-
// 如果是常见的第三方库函数名,则认为是第三方库调用
|
|
1235
|
-
if (common_third_party_functions.includes(callee.name)) {
|
|
1236
|
-
return true;
|
|
790
|
+
// 如果检测到类实例、函数等特殊类型,跳过声明问题检测
|
|
791
|
+
if (type && (type.includes('class-instance') || type.includes('function'))) {
|
|
792
|
+
// 跳过声明问题检测,直接进行命名规范验证
|
|
793
|
+
} else {
|
|
794
|
+
// 检测声明方式问题(优先级较低)
|
|
795
|
+
var original_type = this.detectDeclarationIssue(node);
|
|
796
|
+
if (original_type) {
|
|
797
|
+
// 如果是声明方式问题,直接返回相应的错误,不进行命名规范验证
|
|
798
|
+
var message = this.tip.getDeclarationIssueMessage(original_type, name);
|
|
799
|
+
return {
|
|
800
|
+
name,
|
|
801
|
+
node,
|
|
802
|
+
message,
|
|
803
|
+
error_type: 'declaration',
|
|
804
|
+
severity: 'error',
|
|
805
|
+
fix_suggestion: null,
|
|
806
|
+
original_type
|
|
807
|
+
};
|
|
1237
808
|
}
|
|
809
|
+
}
|
|
1238
810
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
return true;
|
|
1243
|
-
}
|
|
811
|
+
if (!type) {
|
|
812
|
+
type = 'variable'; // 默认类型
|
|
813
|
+
}
|
|
1244
814
|
|
|
1245
|
-
|
|
1246
|
-
|
|
815
|
+
var error = this.validator.validate(name, type);
|
|
816
|
+
if (!error) {
|
|
817
|
+
return null;
|
|
1247
818
|
}
|
|
1248
819
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
console.error("检查第三方库配置参数时出错:", error);
|
|
1252
|
-
return false;
|
|
1253
|
-
}
|
|
1254
|
-
};
|
|
1255
|
-
|
|
1256
|
-
/**
|
|
1257
|
-
* 获取属性类型
|
|
1258
|
-
* @param {object} val_node 属性值节点
|
|
1259
|
-
* @param {string} prop_name 属性名
|
|
1260
|
-
* @param {object} parent_node 父节点
|
|
1261
|
-
* @returns {string} 属性类型
|
|
1262
|
-
*/
|
|
1263
|
-
Detector.prototype._getPropType = function (val_node, prop_name, parent_node) {
|
|
1264
|
-
if (!val_node) {
|
|
1265
|
-
return "undefined_value"; // 未赋值类型
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// 检查是否为null值
|
|
1269
|
-
if (val_node.type === "NullLiteral") {
|
|
1270
|
-
return "null_value"; // null值类型
|
|
1271
|
-
}
|
|
820
|
+
var fix_suggestion = this.corrector.getSuggestion(error);
|
|
821
|
+
var message = this.tip.getFullMessage(error, fix_suggestion);
|
|
1272
822
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
823
|
+
return {
|
|
824
|
+
name,
|
|
825
|
+
node,
|
|
826
|
+
message,
|
|
827
|
+
error_type: error.type,
|
|
828
|
+
severity: error.severity,
|
|
829
|
+
fix_suggestion: fix_suggestion,
|
|
830
|
+
original_type: type
|
|
831
|
+
};
|
|
1280
832
|
}
|
|
1281
833
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
) {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
834
|
+
/**
|
|
835
|
+
* 检测命名类型(仅返回类型,不验证)
|
|
836
|
+
* @param {object} node AST节点
|
|
837
|
+
* @returns {string} 检测到的类型
|
|
838
|
+
*/
|
|
839
|
+
detectTypeOnly(node) {
|
|
840
|
+
var type = null;
|
|
841
|
+
|
|
842
|
+
// 按优先级检测类型 - 变量检测应该在常量检测之前
|
|
843
|
+
var detectors = [
|
|
844
|
+
this.detectClassType.bind(this),
|
|
845
|
+
this.detectClassInstanceType.bind(this),
|
|
846
|
+
this.detectFunctionType.bind(this), // 函数检测应该在变量检测之前
|
|
847
|
+
this.detectConstantType.bind(this), // 常量检测优先级提高
|
|
848
|
+
this.detectVariableType.bind(this), // 变量检测优先级降低
|
|
849
|
+
this.detectParamType.bind(this)
|
|
1298
850
|
];
|
|
1299
|
-
if (special_globals.includes(val_node.name)) {
|
|
1300
|
-
return "value"; // 特殊全局变量应该识别为属性值
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
// 检查是否是类名引用(通常类名使用PascalCase)
|
|
1304
|
-
return "class"; // 属性类类型
|
|
1305
|
-
}
|
|
1306
851
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
// 检查是否为函数调用(可能返回实例)
|
|
1313
|
-
if (val_node.type === "CallExpression") {
|
|
1314
|
-
// 检查是否调用的是构造函数
|
|
1315
|
-
if (
|
|
1316
|
-
val_node.callee &&
|
|
1317
|
-
val_node.callee.type === "Identifier" &&
|
|
1318
|
-
val_node.callee.name &&
|
|
1319
|
-
val_node.callee.name[0] === val_node.callee.name[0].toUpperCase()
|
|
1320
|
-
) {
|
|
1321
|
-
return "instance_class"; // 属性实例类类型
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
// 检查是否是工厂方法(返回实例)
|
|
1325
|
-
var factory_methods = ["create", "make", "build", "get", "fetch"];
|
|
1326
|
-
if (
|
|
1327
|
-
val_node.callee &&
|
|
1328
|
-
val_node.callee.type === "Identifier" &&
|
|
1329
|
-
factory_methods.some(function (method) {
|
|
1330
|
-
return val_node.callee.name.toLowerCase().startsWith(method);
|
|
1331
|
-
})
|
|
1332
|
-
) {
|
|
1333
|
-
return "instance_class"; // 工厂方法返回的实例
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
return "value"; // 默认值类型
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
// 检查是否为数组(包含对象实例)
|
|
1340
|
-
if (val_node.type === "ArrayExpression") {
|
|
1341
|
-
// 检查数组元素是否包含类实例
|
|
1342
|
-
if (val_node.elements && val_node.elements.length > 0) {
|
|
1343
|
-
var has_instance = val_node.elements.some(function (element) {
|
|
1344
|
-
return element && element.type === "NewExpression";
|
|
1345
|
-
});
|
|
1346
|
-
if (has_instance) {
|
|
1347
|
-
return "instance_class"; // 包含实例的数组
|
|
852
|
+
for (var i = 0; i < detectors.length; i++) {
|
|
853
|
+
type = detectors[i](node);
|
|
854
|
+
if (type) {
|
|
855
|
+
break;
|
|
1348
856
|
}
|
|
1349
857
|
}
|
|
1350
|
-
return "value"; // 普通数组
|
|
1351
|
-
}
|
|
1352
858
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
var has_instance = val_node.properties.some(function (property) {
|
|
1358
|
-
return (
|
|
1359
|
-
property && property.value && property.value.type === "NewExpression"
|
|
1360
|
-
);
|
|
1361
|
-
});
|
|
1362
|
-
if (has_instance) {
|
|
1363
|
-
return "instance_class"; // 包含实例的对象
|
|
1364
|
-
}
|
|
859
|
+
// 特殊处理:属性简写应该识别为变量,而不是常量
|
|
860
|
+
if (node.type === 'Property' && node.shorthand && node.value && node.value.type === 'Identifier') {
|
|
861
|
+
// 如果是属性简写,强制识别为变量
|
|
862
|
+
type = 'property-variable';
|
|
1365
863
|
}
|
|
1366
|
-
return "value"; // 普通对象
|
|
1367
|
-
}
|
|
1368
864
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
// 检查模板表达式是否包含类实例
|
|
1372
|
-
if (val_node.expressions && val_node.expressions.length > 0) {
|
|
1373
|
-
var has_instance = val_node.expressions.some(function (expression) {
|
|
1374
|
-
return expression && expression.type === "NewExpression";
|
|
1375
|
-
});
|
|
1376
|
-
if (has_instance) {
|
|
1377
|
-
return "instance_class"; // 包含实例的模板
|
|
1378
|
-
}
|
|
865
|
+
if (!type) {
|
|
866
|
+
type = 'variable'; // 默认类型
|
|
1379
867
|
}
|
|
1380
|
-
return "value"; // 普通模板
|
|
1381
|
-
}
|
|
1382
868
|
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
/**
|
|
1387
|
-
* 检测属性名
|
|
1388
|
-
* @param {string} prop_name 属性名
|
|
1389
|
-
* @param {object} node AST节点
|
|
1390
|
-
* @param {object} val_node 属性值节点
|
|
1391
|
-
* @param {object} parent_node 父节点
|
|
1392
|
-
* @returns {object|null} 错误信息或null
|
|
1393
|
-
*/
|
|
1394
|
-
Detector.prototype._checkPropertyName = function (
|
|
1395
|
-
prop_name,
|
|
1396
|
-
node,
|
|
1397
|
-
val_node,
|
|
1398
|
-
parent_node,
|
|
1399
|
-
) {
|
|
1400
|
-
try {
|
|
1401
|
-
// 检查是否为解构导入中的属性
|
|
1402
|
-
var is_destructured_import = this._isDestructuredImportProperty(
|
|
1403
|
-
node,
|
|
1404
|
-
parent_node,
|
|
1405
|
-
);
|
|
869
|
+
return type;
|
|
870
|
+
}
|
|
1406
871
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
872
|
+
/**
|
|
873
|
+
* 判断是否为字面量值
|
|
874
|
+
* @param {object} node AST节点
|
|
875
|
+
* @returns {boolean} 是否为字面量
|
|
876
|
+
*/
|
|
877
|
+
_isLiteralValue(node) {
|
|
878
|
+
if (!node) {
|
|
879
|
+
return false;
|
|
1410
880
|
}
|
|
1411
881
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
882
|
+
switch (node.type) {
|
|
883
|
+
case 'Literal':
|
|
884
|
+
return !node.regex; // 排除正则表达式
|
|
885
|
+
case 'TemplateLiteral':
|
|
886
|
+
return true;
|
|
887
|
+
case 'Identifier':
|
|
888
|
+
var literal_identifiers = ['undefined', 'null', 'Infinity', 'NaN', 'true', 'false'];
|
|
889
|
+
return literal_identifiers.includes(node.name);
|
|
890
|
+
case 'ArrayExpression':
|
|
891
|
+
if (node.elements && node.elements.length > 0) {
|
|
892
|
+
for (var i = 0; i < node.elements.length; i++) {
|
|
893
|
+
if (!this._isLiteralValue(node.elements[i])) {
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return true;
|
|
899
|
+
case 'ObjectExpression':
|
|
900
|
+
if (node.properties && node.properties.length > 0) {
|
|
901
|
+
for (var i = 0; i < node.properties.length; i++) {
|
|
902
|
+
var property = node.properties[i];
|
|
903
|
+
if (property.value && !this._isLiteralValue(property.value)) {
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return true;
|
|
909
|
+
default:
|
|
910
|
+
return false;
|
|
1421
911
|
}
|
|
912
|
+
}
|
|
1422
913
|
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
914
|
+
/**
|
|
915
|
+
* 判断字符串是否符合PascalCase命名规范
|
|
916
|
+
* @param {string} name 名称
|
|
917
|
+
* @returns {boolean} 是否符合PascalCase
|
|
918
|
+
*/
|
|
919
|
+
_isPascalCase(name) {
|
|
920
|
+
if (!name || name.length === 0) {
|
|
921
|
+
return false;
|
|
1429
922
|
}
|
|
1430
|
-
|
|
1431
|
-
//
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
// 根据属性类型选择对应的规则
|
|
1436
|
-
// 对于null值和未赋值属性,直接跳过命名规范检测
|
|
1437
|
-
if (prop_type === "null_value" || prop_type === "undefined_value") {
|
|
1438
|
-
return null; // 不进行命名规范检测
|
|
923
|
+
|
|
924
|
+
// PascalCase要求首字母大写,后续字母可以大小写混合
|
|
925
|
+
// 但不能包含下划线或连字符
|
|
926
|
+
if (name.includes('_') || name.includes('-')) {
|
|
927
|
+
return false;
|
|
1439
928
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
rule_type = "property-method-name";
|
|
1450
|
-
break;
|
|
1451
|
-
case "value":
|
|
1452
|
-
default:
|
|
1453
|
-
rule_type = "property-value-name";
|
|
1454
|
-
break;
|
|
929
|
+
|
|
930
|
+
// 首字母必须是大写字母
|
|
931
|
+
if (!/^[A-Z]/.test(name)) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// 整体必须符合标识符规范
|
|
936
|
+
if (!/^[A-Za-z][A-Za-z0-9]*$/.test(name)) {
|
|
937
|
+
return false;
|
|
1455
938
|
}
|
|
939
|
+
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
1456
942
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
943
|
+
/**
|
|
944
|
+
* 检查属性名是否在作用域内有对应的类实例变量声明
|
|
945
|
+
* @param {string} name 属性名
|
|
946
|
+
* @param {object} node AST节点
|
|
947
|
+
* @returns {boolean} 是否是类实例引用
|
|
948
|
+
*/
|
|
949
|
+
_isClassInstanceReference(name, node) {
|
|
950
|
+
if (!name || !node) {
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// 遍历AST查找变量声明
|
|
955
|
+
var current_node = node;
|
|
956
|
+
while (current_node.parent) {
|
|
957
|
+
current_node = current_node.parent;
|
|
958
|
+
|
|
959
|
+
// 查找变量声明
|
|
960
|
+
if (current_node.type === 'VariableDeclaration') {
|
|
961
|
+
for (var i = 0; i < current_node.declarations.length; i++) {
|
|
962
|
+
var decl = current_node.declarations[i];
|
|
963
|
+
if (decl.id && decl.id.type === 'Identifier' && decl.id.name === name) {
|
|
964
|
+
// 检查是否为类实例声明(new表达式)
|
|
965
|
+
if (decl.init && decl.init.type === 'NewExpression') {
|
|
966
|
+
return true;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
1467
970
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
971
|
+
|
|
972
|
+
// 查找赋值表达式
|
|
973
|
+
if (current_node.type === 'AssignmentExpression' &&
|
|
974
|
+
current_node.left && current_node.left.type === 'Identifier' &&
|
|
975
|
+
current_node.left.name === name) {
|
|
976
|
+
// 检查是否为类实例赋值(new表达式)
|
|
977
|
+
if (current_node.right && current_node.right.type === 'NewExpression') {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
1471
980
|
}
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
981
|
+
|
|
982
|
+
// 如果到达程序根节点,停止搜索
|
|
983
|
+
if (current_node.type === 'Program') {
|
|
984
|
+
break;
|
|
1475
985
|
}
|
|
1476
986
|
}
|
|
987
|
+
|
|
988
|
+
return false;
|
|
989
|
+
}
|
|
1477
990
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
if (!rule) {
|
|
991
|
+
/**
|
|
992
|
+
* 通用类型溯源:对于导出语句中的属性,溯源其引用的类型
|
|
993
|
+
* @param {object} node 属性节点
|
|
994
|
+
* @returns {string} 溯源到的类型,无法溯源则返回null
|
|
995
|
+
*/
|
|
996
|
+
_traceReferenceType(node) {
|
|
997
|
+
if (!node || !node.key || node.key.type !== 'Identifier') {
|
|
1486
998
|
return null;
|
|
1487
999
|
}
|
|
1488
|
-
|
|
1489
|
-
//
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
if (rule_type === "property-value-name") {
|
|
1493
|
-
// 对于属性值名,智能识别属性值类型并应用相应的命名规范
|
|
1494
|
-
style_err = this._checkPropertyValueStyle(prop_name, val_node, rule_type);
|
|
1495
|
-
} else if (rule_type === "property-method-name") {
|
|
1496
|
-
// 属性方法:智能识别方法类型并提供准确的命名规范
|
|
1497
|
-
style_err = this._checkPropertyMethodStyle(
|
|
1498
|
-
prop_name,
|
|
1499
|
-
val_node,
|
|
1500
|
-
rule_type,
|
|
1501
|
-
);
|
|
1502
|
-
} else if (rule_type === "property-instance-class-name") {
|
|
1503
|
-
// 属性实例类:智能识别实例类并提供准确的命名规范
|
|
1504
|
-
style_err = this._checkPropertyInstanceClassStyle(
|
|
1505
|
-
prop_name,
|
|
1506
|
-
val_node,
|
|
1507
|
-
rule_type,
|
|
1508
|
-
);
|
|
1509
|
-
} else if (rule_type === "property-class-name") {
|
|
1510
|
-
// 属性类:智能识别类引用并提供准确的命名规范
|
|
1511
|
-
style_err = this._checkPropertyClassStyle(prop_name, val_node, rule_type);
|
|
1512
|
-
} else {
|
|
1513
|
-
// 其他属性类型:使用原有逻辑
|
|
1514
|
-
style_err = this._validate_naming_style(
|
|
1515
|
-
prop_name,
|
|
1516
|
-
rule.styles,
|
|
1517
|
-
rule_type,
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
if (style_err) {
|
|
1522
|
-
return {
|
|
1523
|
-
node: node,
|
|
1524
|
-
message: style_err,
|
|
1525
|
-
rule_type: rule_type, // 添加规则类型信息
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
// 检查长度
|
|
1530
|
-
var len_err = this._checkNameLength(
|
|
1531
|
-
prop_name,
|
|
1532
|
-
rule.min,
|
|
1533
|
-
rule.max,
|
|
1534
|
-
rule_type,
|
|
1535
|
-
);
|
|
1536
|
-
if (len_err) {
|
|
1537
|
-
// 使用智能缩写生成推荐名称
|
|
1538
|
-
var smart_abbreviation = this._getSmartAbbreviation(prop_name, rule_type);
|
|
1539
|
-
var error_message = len_err;
|
|
1540
|
-
if (smart_abbreviation && smart_abbreviation !== prop_name) {
|
|
1541
|
-
error_message = len_err + ",建议使用: " + smart_abbreviation;
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
return {
|
|
1545
|
-
node: node,
|
|
1546
|
-
message: error_message,
|
|
1547
|
-
rule_type: rule_type, // 添加规则类型信息
|
|
1548
|
-
};
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
// 检查单词长度
|
|
1552
|
-
// 对于私有属性值名(以_开头且长度>2),跳过单词长度检查,视为单个单词
|
|
1553
|
-
var is_private_property = prop_name.startsWith("_");
|
|
1554
|
-
if (!(is_private_property && prop_name.length > 2)) {
|
|
1555
|
-
var word_err = this._checkWordLength(
|
|
1556
|
-
prop_name,
|
|
1557
|
-
rule.single_word_len,
|
|
1558
|
-
rule_type,
|
|
1559
|
-
);
|
|
1560
|
-
if (word_err) {
|
|
1561
|
-
// 检查错误消息是否已经包含建议
|
|
1562
|
-
var error_message = word_err;
|
|
1563
|
-
if (!word_err.includes("建议使用")) {
|
|
1564
|
-
// 如果错误消息中不包含建议,则添加建议
|
|
1565
|
-
var smart_abbreviation = this._getSmartAbbreviation(
|
|
1566
|
-
prop_name,
|
|
1567
|
-
rule_type,
|
|
1568
|
-
);
|
|
1569
|
-
if (smart_abbreviation && smart_abbreviation !== prop_name) {
|
|
1570
|
-
error_message = word_err + ",建议使用: " + smart_abbreviation;
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
return {
|
|
1575
|
-
node: node,
|
|
1576
|
-
message: error_message,
|
|
1577
|
-
rule_type: rule_type, // 添加规则类型信息
|
|
1578
|
-
};
|
|
1579
|
-
}
|
|
1000
|
+
|
|
1001
|
+
// 对于简写属性 { AdminManager },溯源属性名对应的类型
|
|
1002
|
+
if (node.shorthand) {
|
|
1003
|
+
return this._traceIdentifierType(node.key.name, node);
|
|
1580
1004
|
}
|
|
1581
|
-
|
|
1582
|
-
//
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
return {
|
|
1586
|
-
node: node,
|
|
1587
|
-
message: forbid_err,
|
|
1588
|
-
rule_type: rule_type, // 添加规则类型信息
|
|
1589
|
-
};
|
|
1005
|
+
|
|
1006
|
+
// 对于完整属性 { Admin_DD: Admin },溯源属性值对应的类型
|
|
1007
|
+
if (!node.shorthand && node.value && node.value.type === 'Identifier') {
|
|
1008
|
+
return this._traceIdentifierType(node.value.name, node);
|
|
1590
1009
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
} catch (error) {
|
|
1594
|
-
console.error("检测属性名时出错:", error);
|
|
1010
|
+
|
|
1011
|
+
// 其他情况无法溯源
|
|
1595
1012
|
return null;
|
|
1596
1013
|
}
|
|
1597
|
-
};
|
|
1598
1014
|
|
|
1599
|
-
/**
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
var rec_words = this.config.getRecommendedWords(name_type);
|
|
1608
|
-
if (!rec_words || Object.keys(rec_words).length === 0) {
|
|
1015
|
+
/**
|
|
1016
|
+
* 溯源标识符的类型
|
|
1017
|
+
* @param {string} name 标识符名
|
|
1018
|
+
* @param {object} node AST节点
|
|
1019
|
+
* @returns {string} 溯源到的类型,无法溯源则返回null
|
|
1020
|
+
*/
|
|
1021
|
+
_traceIdentifierType(name, node) {
|
|
1022
|
+
if (!name || !node) {
|
|
1609
1023
|
return null;
|
|
1610
1024
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
var
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1025
|
+
|
|
1026
|
+
// 获取Program节点(AST根节点)
|
|
1027
|
+
var program_node = this._getProgramNode(node);
|
|
1028
|
+
if (!program_node || !program_node.body) {
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// 遍历同级节点查找声明
|
|
1033
|
+
for (var i = 0; i < program_node.body.length; i++) {
|
|
1034
|
+
var sibling_node = program_node.body[i];
|
|
1035
|
+
|
|
1036
|
+
// 查找类声明
|
|
1037
|
+
if (sibling_node.type === 'ClassDeclaration') {
|
|
1038
|
+
if (sibling_node.id && sibling_node.id.type === 'Identifier' && sibling_node.id.name === name) {
|
|
1039
|
+
return 'class';
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// 查找函数声明
|
|
1044
|
+
if (sibling_node.type === 'FunctionDeclaration') {
|
|
1045
|
+
if (sibling_node.id && sibling_node.id.type === 'Identifier' && sibling_node.id.name === name) {
|
|
1046
|
+
return 'function';
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// 查找变量声明
|
|
1051
|
+
if (sibling_node.type === 'VariableDeclaration') {
|
|
1052
|
+
for (var j = 0; j < sibling_node.declarations.length; j++) {
|
|
1053
|
+
var decl = sibling_node.declarations[j];
|
|
1054
|
+
if (decl.id && decl.id.type === 'Identifier' && decl.id.name === name) {
|
|
1055
|
+
// 检查变量声明的类型
|
|
1056
|
+
if (decl.init) {
|
|
1057
|
+
// 类表达式
|
|
1058
|
+
if (decl.init.type === 'ClassExpression') {
|
|
1059
|
+
return 'class';
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// 函数表达式
|
|
1063
|
+
if (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression') {
|
|
1064
|
+
return 'function';
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// new 表达式(类实例)
|
|
1068
|
+
if (decl.init.type === 'NewExpression') {
|
|
1069
|
+
return 'class-instance';
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// 常量(只有const声明且为基础字面量)
|
|
1073
|
+
if (sibling_node.kind === 'const' && this._isBasicLiteralValue(decl.init)) {
|
|
1074
|
+
return 'constant';
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// 普通变量
|
|
1078
|
+
return 'variable';
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// 没有初始值的变量声明,默认为变量
|
|
1082
|
+
return 'variable';
|
|
1651
1083
|
}
|
|
1652
1084
|
}
|
|
1653
1085
|
}
|
|
1654
1086
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
} catch (error) {
|
|
1658
|
-
console.error("检查并推荐更合适的词汇时出错:", error);
|
|
1087
|
+
|
|
1088
|
+
// 无法溯源到具体类型
|
|
1659
1089
|
return null;
|
|
1660
1090
|
}
|
|
1661
|
-
};
|
|
1662
1091
|
|
|
1663
|
-
/**
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
if (!
|
|
1092
|
+
/**
|
|
1093
|
+
* 检查属性名是否在作用域内有对应的类声明
|
|
1094
|
+
* @param {string} name 属性名
|
|
1095
|
+
* @param {object} node AST节点
|
|
1096
|
+
* @returns {boolean} 是否是类引用
|
|
1097
|
+
*/
|
|
1098
|
+
_isClassReference(name, node) {
|
|
1099
|
+
if (!name || !node) {
|
|
1671
1100
|
return false;
|
|
1672
1101
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1102
|
+
|
|
1103
|
+
console.log(' [DEBUG] _isClassReference called:', name);
|
|
1104
|
+
|
|
1105
|
+
// 在ESLint环境中,AST节点有parent属性,可以向上遍历
|
|
1106
|
+
// 获取Program节点(AST根节点)
|
|
1107
|
+
var program_node = this._getProgramNode(node);
|
|
1108
|
+
console.log(' [DEBUG] Program node:', program_node ? program_node.type : 'null');
|
|
1109
|
+
|
|
1110
|
+
if (!program_node || !program_node.body) {
|
|
1111
|
+
console.log(' [DEBUG] No program node or body');
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
console.log(' [DEBUG] Program body length:', program_node.body.length);
|
|
1116
|
+
|
|
1117
|
+
// 遍历同级节点查找类声明
|
|
1118
|
+
for (var i = 0; i < program_node.body.length; i++) {
|
|
1119
|
+
var sibling_node = program_node.body[i];
|
|
1120
|
+
console.log(' [DEBUG] Sibling node', i, ':', sibling_node.type);
|
|
1121
|
+
|
|
1122
|
+
// 查找类声明
|
|
1123
|
+
if (sibling_node.type === 'ClassDeclaration') {
|
|
1124
|
+
console.log(' [DEBUG] Found ClassDeclaration:', sibling_node.id ? sibling_node.id.name : 'null');
|
|
1125
|
+
if (sibling_node.id && sibling_node.id.type === 'Identifier' && sibling_node.id.name === name) {
|
|
1126
|
+
console.log(' [DEBUG] Class reference found!');
|
|
1127
|
+
return true;
|
|
1690
1128
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// 查找变量声明中的类表达式
|
|
1132
|
+
if (sibling_node.type === 'VariableDeclaration') {
|
|
1133
|
+
for (var j = 0; j < sibling_node.declarations.length; j++) {
|
|
1134
|
+
var decl = sibling_node.declarations[j];
|
|
1135
|
+
if (decl.id && decl.id.type === 'Identifier' && decl.id.name === name) {
|
|
1136
|
+
// 检查是否为类表达式
|
|
1137
|
+
if (decl.init && decl.init.type === 'ClassExpression') {
|
|
1138
|
+
return true;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1694
1143
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
} catch (error) {
|
|
1698
|
-
console.error("检查值是否为基础值类型时出错:", error);
|
|
1144
|
+
|
|
1145
|
+
console.log(' [DEBUG] No class reference found');
|
|
1699
1146
|
return false;
|
|
1700
1147
|
}
|
|
1701
|
-
};
|
|
1702
|
-
|
|
1703
|
-
/**
|
|
1704
|
-
* 检查是否应该使用let而不是const声明变量
|
|
1705
|
-
* @param {object} node 变量声明节点
|
|
1706
|
-
* @param {object} val_node 值节点
|
|
1707
|
-
* @returns {boolean} 是否应该使用let
|
|
1708
|
-
*/
|
|
1709
|
-
Detector.prototype._shouldUseLetInsteadOfConst = function (node, val_node) {
|
|
1710
|
-
try {
|
|
1711
|
-
if (!node || !val_node) {
|
|
1712
|
-
return false;
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
// 检查是否为模块导入(需要排除的情况)
|
|
1716
|
-
var is_module_import = this._isModuleImport(node, val_node);
|
|
1717
|
-
if (is_module_import) {
|
|
1718
|
-
return false;
|
|
1719
|
-
}
|
|
1720
1148
|
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1149
|
+
/**
|
|
1150
|
+
* 获取Program节点(AST根节点)
|
|
1151
|
+
* @param {object} node AST节点
|
|
1152
|
+
* @returns {object} Program节点
|
|
1153
|
+
*/
|
|
1154
|
+
_getProgramNode(node) {
|
|
1155
|
+
var current_node = node;
|
|
1156
|
+
|
|
1157
|
+
// 在ESLint环境中,AST节点有parent属性,可以向上遍历
|
|
1158
|
+
// 向上遍历直到找到Program节点
|
|
1159
|
+
while (current_node && current_node.parent) {
|
|
1160
|
+
current_node = current_node.parent;
|
|
1161
|
+
|
|
1162
|
+
// 如果找到Program节点,返回它
|
|
1163
|
+
if (current_node.type === 'Program') {
|
|
1164
|
+
return current_node;
|
|
1731
1165
|
}
|
|
1732
1166
|
}
|
|
1167
|
+
|
|
1168
|
+
// 如果没有找到Program节点,返回null
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1733
1171
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
// 检查是否为函数调用
|
|
1742
|
-
if (val_node.type === "CallExpression") {
|
|
1743
|
-
should_use_let = true;
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
// 检查是否为变量引用
|
|
1747
|
-
if (val_node.type === "Identifier") {
|
|
1748
|
-
should_use_let = true;
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
// 检查是否为解构赋值且右侧是复杂类型
|
|
1752
|
-
var is_complex_type = this._isComplexType(val_node);
|
|
1753
|
-
if (is_destructuring && is_complex_type) {
|
|
1754
|
-
should_use_let = true;
|
|
1755
|
-
}
|
|
1172
|
+
/**
|
|
1173
|
+
* 获取父级声明类型
|
|
1174
|
+
* @param {object} node AST节点
|
|
1175
|
+
* @returns {string} 声明类型
|
|
1176
|
+
*/
|
|
1177
|
+
_getParentDeclarationType(node) {
|
|
1178
|
+
var current_node = node;
|
|
1756
1179
|
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
console.error("检查是否应该使用let时出错:", error);
|
|
1760
|
-
return false;
|
|
1761
|
-
}
|
|
1762
|
-
};
|
|
1180
|
+
while (current_node.parent) {
|
|
1181
|
+
current_node = current_node.parent;
|
|
1763
1182
|
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
* @returns {boolean} 是否为复杂类型
|
|
1768
|
-
*/
|
|
1769
|
-
Detector.prototype._isComplexType = function (val_node) {
|
|
1770
|
-
try {
|
|
1771
|
-
if (!val_node) {
|
|
1772
|
-
return false;
|
|
1773
|
-
}
|
|
1183
|
+
if (current_node.type === 'VariableDeclarator' && current_node.parent && current_node.parent.kind) {
|
|
1184
|
+
return current_node.parent.kind;
|
|
1185
|
+
}
|
|
1774
1186
|
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
switch (val_node.type) {
|
|
1779
|
-
case "FunctionExpression": // 函数表达式
|
|
1780
|
-
case "ArrowFunctionExpression": // 箭头函数
|
|
1781
|
-
case "NewExpression": // new 实例化
|
|
1782
|
-
case "CallExpression": // 函数调用
|
|
1783
|
-
case "MemberExpression": // 成员表达式
|
|
1784
|
-
case "Identifier": // 标识符(可能是变量或函数)
|
|
1785
|
-
case "ClassExpression": // 类表达式
|
|
1786
|
-
case "ThisExpression": // this表达式
|
|
1787
|
-
case "TemplateLiteral": // 模板字符串
|
|
1788
|
-
is_complex_type = true;
|
|
1789
|
-
break;
|
|
1790
|
-
default:
|
|
1791
|
-
is_complex_type = false;
|
|
1187
|
+
if (current_node.type === 'AssignmentExpression') {
|
|
1188
|
+
return 'variable';
|
|
1189
|
+
}
|
|
1792
1190
|
}
|
|
1793
1191
|
|
|
1794
|
-
return
|
|
1795
|
-
} catch (error) {
|
|
1796
|
-
console.error("检查值是否为复杂类型时出错:", error);
|
|
1797
|
-
return false;
|
|
1192
|
+
return 'unknown';
|
|
1798
1193
|
}
|
|
1799
|
-
};
|
|
1800
1194
|
|
|
1801
|
-
/**
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
try {
|
|
1809
|
-
if (!node || !val_node) {
|
|
1810
|
-
return false;
|
|
1811
|
-
}
|
|
1195
|
+
/**
|
|
1196
|
+
* 获取父级对象节点
|
|
1197
|
+
* @param {object} node AST节点
|
|
1198
|
+
* @returns {object|null} 父级对象节点
|
|
1199
|
+
*/
|
|
1200
|
+
_getParentObject(node) {
|
|
1201
|
+
var current_node = node;
|
|
1812
1202
|
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
// 检查是否为require调用
|
|
1816
|
-
if (
|
|
1817
|
-
val_node.callee.type === "Identifier" &&
|
|
1818
|
-
val_node.callee.name === "require"
|
|
1819
|
-
) {
|
|
1820
|
-
return true;
|
|
1821
|
-
}
|
|
1203
|
+
while (current_node.parent) {
|
|
1204
|
+
current_node = current_node.parent;
|
|
1822
1205
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
return true;
|
|
1206
|
+
if (current_node.type === 'ObjectExpression') {
|
|
1207
|
+
return current_node;
|
|
1826
1208
|
}
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
// 情况2:const {xxx} = require('module') - 解构模块导入
|
|
1830
|
-
if (node.parent && node.parent.type === "VariableDeclarator") {
|
|
1831
|
-
var declarator = node.parent;
|
|
1832
1209
|
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
// 检查右侧是否为require调用
|
|
1836
|
-
if (val_node.type === "CallExpression" && val_node.callee) {
|
|
1837
|
-
if (
|
|
1838
|
-
val_node.callee.type === "Identifier" &&
|
|
1839
|
-
val_node.callee.name === "require"
|
|
1840
|
-
) {
|
|
1841
|
-
return true;
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1210
|
+
if (current_node.type === 'VariableDeclarator' || current_node.type === 'AssignmentExpression') {
|
|
1211
|
+
break;
|
|
1844
1212
|
}
|
|
1845
1213
|
}
|
|
1846
1214
|
|
|
1847
|
-
return
|
|
1848
|
-
} catch (error) {
|
|
1849
|
-
console.error("检查是否为模块导入时出错:", error);
|
|
1850
|
-
return false;
|
|
1215
|
+
return null;
|
|
1851
1216
|
}
|
|
1852
|
-
};
|
|
1853
1217
|
|
|
1854
|
-
/**
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
if (!node || node.type !== "Identifier") {
|
|
1218
|
+
/**
|
|
1219
|
+
* 判断是否为基础字面量值(排除正则表达式)
|
|
1220
|
+
* @param {object} node AST节点
|
|
1221
|
+
* @returns {boolean} 是否为基础字面量
|
|
1222
|
+
*/
|
|
1223
|
+
_isBasicLiteralValue(node) {
|
|
1224
|
+
if (!node) {
|
|
1862
1225
|
return false;
|
|
1863
1226
|
}
|
|
1864
1227
|
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
var declarator = declarators[i];
|
|
1878
|
-
if (declarator.init && declarator.init.type === "CallExpression") {
|
|
1879
|
-
var init_call = declarator.init;
|
|
1880
|
-
if (
|
|
1881
|
-
init_call.callee &&
|
|
1882
|
-
init_call.callee.type === "Identifier" &&
|
|
1883
|
-
init_call.callee.name === "require"
|
|
1884
|
-
) {
|
|
1885
|
-
return true;
|
|
1228
|
+
switch (node.type) {
|
|
1229
|
+
case 'Literal':
|
|
1230
|
+
// 排除正则表达式
|
|
1231
|
+
return !node.regex;
|
|
1232
|
+
case 'TemplateLiteral':
|
|
1233
|
+
return true;
|
|
1234
|
+
case 'ArrayExpression':
|
|
1235
|
+
// 数组必须所有元素都是基础字面量
|
|
1236
|
+
if (node.elements && node.elements.length > 0) {
|
|
1237
|
+
for (var i = 0; i < node.elements.length; i++) {
|
|
1238
|
+
if (!this._isBasicLiteralValue(node.elements[i])) {
|
|
1239
|
+
return false;
|
|
1886
1240
|
}
|
|
1887
1241
|
}
|
|
1888
1242
|
}
|
|
1889
|
-
|
|
1243
|
+
return true;
|
|
1244
|
+
case 'ObjectExpression':
|
|
1245
|
+
// 对象必须所有属性值都是基础字面量
|
|
1246
|
+
if (node.properties && node.properties.length > 0) {
|
|
1247
|
+
for (var i = 0; i < node.properties.length; i++) {
|
|
1248
|
+
var property = node.properties[i];
|
|
1249
|
+
if (property.value && !this._isBasicLiteralValue(property.value)) {
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return true;
|
|
1255
|
+
case 'Identifier':
|
|
1256
|
+
var basic_literal_identifiers = ['undefined', 'null', 'Infinity', 'NaN', 'true', 'false'];
|
|
1257
|
+
return basic_literal_identifiers.includes(node.name);
|
|
1258
|
+
default:
|
|
1259
|
+
return false;
|
|
1890
1260
|
}
|
|
1891
|
-
|
|
1892
|
-
return false;
|
|
1893
|
-
} catch (error) {
|
|
1894
|
-
console.error("检查是否为模块导入变量时出错:", error);
|
|
1895
|
-
return false;
|
|
1896
1261
|
}
|
|
1897
|
-
};
|
|
1898
1262
|
|
|
1899
|
-
/**
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
if (!node || !node.parent) {
|
|
1263
|
+
/**
|
|
1264
|
+
* 检查对象的所有属性是否都是字面量
|
|
1265
|
+
* @param {object} object_node 对象节点
|
|
1266
|
+
* @returns {boolean} 是否所有属性都是字面量
|
|
1267
|
+
*/
|
|
1268
|
+
_isObjectAllLiteral(object_node) {
|
|
1269
|
+
if (object_node.type !== 'ObjectExpression' || !object_node.properties) {
|
|
1907
1270
|
return false;
|
|
1908
1271
|
}
|
|
1909
1272
|
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
// 情况1:节点是Property.key(解构赋值中的属性键)
|
|
1914
|
-
if (node.parent.type === "Property" && node.parent.key === node) {
|
|
1915
|
-
var property = node.parent;
|
|
1916
|
-
|
|
1917
|
-
// 检查Property的父节点是否为ObjectPattern
|
|
1918
|
-
if (property.parent && property.parent.type === "ObjectPattern") {
|
|
1919
|
-
var object_pattern = property.parent;
|
|
1273
|
+
for (var i = 0; i < object_node.properties.length; i++) {
|
|
1274
|
+
var property = object_node.properties[i];
|
|
1920
1275
|
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
) {
|
|
1926
|
-
var declarator = object_pattern.parent;
|
|
1927
|
-
|
|
1928
|
-
// 检查右侧是否为require调用或其他模块导入
|
|
1929
|
-
if (declarator.init) {
|
|
1930
|
-
var init_type = declarator.init.type;
|
|
1931
|
-
|
|
1932
|
-
// 常见的模块导入方式
|
|
1933
|
-
if (
|
|
1934
|
-
init_type === "CallExpression" &&
|
|
1935
|
-
declarator.init.callee &&
|
|
1936
|
-
declarator.init.callee.name === "require"
|
|
1937
|
-
) {
|
|
1938
|
-
is_destructured = true;
|
|
1939
|
-
}
|
|
1276
|
+
// 跳过解构赋值
|
|
1277
|
+
if (property.type !== 'Property') {
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1940
1280
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1281
|
+
// 检查属性值是否为字面量
|
|
1282
|
+
if (!this._isLiteralValue(property.value)) {
|
|
1283
|
+
return false;
|
|
1947
1284
|
}
|
|
1948
1285
|
}
|
|
1949
1286
|
|
|
1950
|
-
// 情况2:节点是VariableDeclarator(简单变量声明)
|
|
1951
|
-
else if (node.parent.type === "VariableDeclarator") {
|
|
1952
|
-
var declarator = node.parent;
|
|
1953
|
-
|
|
1954
|
-
// 检查是否为解构模式(ObjectPattern或ArrayPattern)
|
|
1955
|
-
if (declarator.id && declarator.id.type === "ObjectPattern") {
|
|
1956
|
-
// 检查右侧是否为require调用或其他模块导入
|
|
1957
|
-
if (declarator.init) {
|
|
1958
|
-
var init_type = declarator.init.type;
|
|
1959
|
-
|
|
1960
|
-
// 常见的模块导入方式
|
|
1961
|
-
if (
|
|
1962
|
-
init_type === "CallExpression" &&
|
|
1963
|
-
declarator.init.callee &&
|
|
1964
|
-
declarator.init.callee.name === "require"
|
|
1965
|
-
) {
|
|
1966
|
-
is_destructured = true;
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
// 如果是ImportDeclaration(ES6导入)
|
|
1970
|
-
if (init_type === "ImportExpression") {
|
|
1971
|
-
is_destructured = true;
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
|
-
return is_destructured;
|
|
1978
|
-
} catch (error) {
|
|
1979
|
-
console.error("检查是否为解构导入时出错:", error);
|
|
1980
|
-
return false;
|
|
1981
|
-
}
|
|
1982
|
-
};
|
|
1983
|
-
|
|
1984
|
-
/**
|
|
1985
|
-
* 检查是否为解构导入中的属性
|
|
1986
|
-
* @param {object} node 属性节点
|
|
1987
|
-
* @param {object} parent_node 父节点
|
|
1988
|
-
* @returns {boolean} 是否为解构导入中的属性
|
|
1989
|
-
*/
|
|
1990
|
-
Detector.prototype._isDestructuredImportProperty = function (
|
|
1991
|
-
node,
|
|
1992
|
-
parent_node,
|
|
1993
|
-
) {
|
|
1994
|
-
try {
|
|
1995
|
-
// 处理新的父节点信息结构(包含node和parent属性)
|
|
1996
|
-
var actual_parent_node = parent_node;
|
|
1997
|
-
if (parent_node && typeof parent_node === "object" && parent_node.node) {
|
|
1998
|
-
actual_parent_node = parent_node.node;
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
if (!node || !actual_parent_node) {
|
|
2002
|
-
return false;
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
// 检查父节点是否为ObjectPattern(解构模式)
|
|
2006
|
-
if (actual_parent_node.type === "ObjectPattern") {
|
|
2007
|
-
// 检查父节点的父节点是否为VariableDeclarator
|
|
2008
|
-
var actual_parent_parent =
|
|
2009
|
-
parent_node && typeof parent_node === "object" && parent_node.parent
|
|
2010
|
-
? parent_node.parent
|
|
2011
|
-
: actual_parent_node.parent;
|
|
2012
|
-
|
|
2013
|
-
if (
|
|
2014
|
-
actual_parent_parent &&
|
|
2015
|
-
actual_parent_parent.type === "VariableDeclarator"
|
|
2016
|
-
) {
|
|
2017
|
-
var declarator = actual_parent_parent;
|
|
2018
|
-
|
|
2019
|
-
// 检查右侧是否为require调用或其他模块导入
|
|
2020
|
-
if (declarator.init) {
|
|
2021
|
-
var init_type = declarator.init.type;
|
|
2022
|
-
|
|
2023
|
-
// 常见的模块导入方式
|
|
2024
|
-
if (
|
|
2025
|
-
init_type === "CallExpression" &&
|
|
2026
|
-
declarator.init.callee &&
|
|
2027
|
-
declarator.init.callee.name === "require"
|
|
2028
|
-
) {
|
|
2029
|
-
return true;
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
// 如果是ImportDeclaration(ES6导入)
|
|
2033
|
-
if (init_type === "ImportExpression") {
|
|
2034
|
-
return true;
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
|
|
2040
|
-
return false;
|
|
2041
|
-
} catch (error) {
|
|
2042
|
-
console.error("检查是否为解构导入中的属性时出错:", error);
|
|
2043
|
-
return false;
|
|
2044
|
-
}
|
|
2045
|
-
};
|
|
2046
|
-
|
|
2047
|
-
/**
|
|
2048
|
-
* 检查是否为函数赋值(函数表达式或箭头函数)
|
|
2049
|
-
* @param {object} node AST节点
|
|
2050
|
-
* @returns {boolean} 是否为函数赋值
|
|
2051
|
-
*/
|
|
2052
|
-
Detector.prototype._isFunctionAssignment = function (node) {
|
|
2053
|
-
try {
|
|
2054
|
-
if (!node) {
|
|
2055
|
-
return false;
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
// 检查节点是否为解构赋值中的属性
|
|
2059
|
-
if (node.type === "Property" && node.key === node) {
|
|
2060
|
-
// 检查父节点是否为ObjectPattern
|
|
2061
|
-
if (node.parent && node.parent.type === "ObjectPattern") {
|
|
2062
|
-
// 检查父节点的父节点是否为VariableDeclarator
|
|
2063
|
-
if (
|
|
2064
|
-
node.parent.parent &&
|
|
2065
|
-
node.parent.parent.type === "VariableDeclarator"
|
|
2066
|
-
) {
|
|
2067
|
-
var declarator = node.parent.parent;
|
|
2068
|
-
|
|
2069
|
-
// 检查右侧是否为函数表达式或箭头函数
|
|
2070
|
-
if (declarator.init) {
|
|
2071
|
-
var init_type = declarator.init.type;
|
|
2072
|
-
|
|
2073
|
-
// 函数表达式
|
|
2074
|
-
if (init_type === "FunctionExpression") {
|
|
2075
|
-
return true;
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
// 箭头函数
|
|
2079
|
-
if (init_type === "ArrowFunctionExpression") {
|
|
2080
|
-
return true;
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
// 函数调用(可能返回函数)
|
|
2084
|
-
if (init_type === "CallExpression") {
|
|
2085
|
-
return true;
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
// 检查节点是否为解构赋值中的数组元素
|
|
2093
|
-
if (
|
|
2094
|
-
node.type === "Identifier" &&
|
|
2095
|
-
node.parent &&
|
|
2096
|
-
node.parent.type === "ArrayPattern"
|
|
2097
|
-
) {
|
|
2098
|
-
// 检查父节点的父节点是否为VariableDeclarator
|
|
2099
|
-
if (
|
|
2100
|
-
node.parent.parent &&
|
|
2101
|
-
node.parent.parent.type === "VariableDeclarator"
|
|
2102
|
-
) {
|
|
2103
|
-
var declarator = node.parent.parent;
|
|
2104
|
-
|
|
2105
|
-
// 检查右侧是否为函数表达式或箭头函数
|
|
2106
|
-
if (declarator.init) {
|
|
2107
|
-
var init_type = declarator.init.type;
|
|
2108
|
-
|
|
2109
|
-
// 函数表达式
|
|
2110
|
-
if (init_type === "FunctionExpression") {
|
|
2111
|
-
return true;
|
|
2112
|
-
}
|
|
2113
|
-
|
|
2114
|
-
// 箭头函数
|
|
2115
|
-
if (init_type === "ArrowFunctionExpression") {
|
|
2116
|
-
return true;
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
|
-
// 函数调用(可能返回函数)
|
|
2120
|
-
if (init_type === "CallExpression") {
|
|
2121
|
-
return true;
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
return false;
|
|
2128
|
-
} catch (error) {
|
|
2129
|
-
console.error("检查是否为函数赋值时出错:", error);
|
|
2130
|
-
return false;
|
|
2131
|
-
}
|
|
2132
|
-
};
|
|
2133
|
-
|
|
2134
|
-
/**
|
|
2135
|
-
* 检查常量名是否符合规范
|
|
2136
|
-
* @param {string} constant_name 常量名
|
|
2137
|
-
* @returns {boolean} 是否符合规范
|
|
2138
|
-
*/
|
|
2139
|
-
Detector.prototype._isConstantNameValid = function (constant_name) {
|
|
2140
|
-
try {
|
|
2141
|
-
var rule = this.config.getRule("constant-name");
|
|
2142
|
-
if (!rule) {
|
|
2143
|
-
return false;
|
|
2144
|
-
}
|
|
2145
|
-
|
|
2146
|
-
// 检查命名风格
|
|
2147
|
-
var style_err = this._validate_naming_style(
|
|
2148
|
-
constant_name,
|
|
2149
|
-
rule.styles,
|
|
2150
|
-
"constant-name",
|
|
2151
|
-
);
|
|
2152
|
-
return !style_err;
|
|
2153
|
-
} catch (error) {
|
|
2154
|
-
console.error("检查常量名是否符合规范时出错:", error);
|
|
2155
|
-
return false;
|
|
2156
|
-
}
|
|
2157
|
-
};
|
|
2158
|
-
|
|
2159
|
-
/**
|
|
2160
|
-
* 检查是否为忽略词
|
|
2161
|
-
* @param {string} name 名称
|
|
2162
|
-
* @param {string} name_type 名称类型
|
|
2163
|
-
* @returns {boolean} 是否为忽略词
|
|
2164
|
-
*/
|
|
2165
|
-
Detector.prototype._isIgnoredWord = function (name, name_type) {
|
|
2166
|
-
try {
|
|
2167
|
-
var ignored_words = this.config.getIgnoredWords(name_type);
|
|
2168
|
-
if (!ignored_words || !Array.isArray(ignored_words)) {
|
|
2169
|
-
return false;
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
|
-
var name_lower = name.toLowerCase();
|
|
2173
|
-
for (var i = 0; i < ignored_words.length; i++) {
|
|
2174
|
-
if (ignored_words[i].toLowerCase() === name_lower) {
|
|
2175
|
-
return true;
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
return false;
|
|
2180
|
-
} catch (error) {
|
|
2181
|
-
console.error("检查是否为忽略词时出错:", error);
|
|
2182
|
-
return false;
|
|
2183
|
-
}
|
|
2184
|
-
};
|
|
2185
|
-
|
|
2186
|
-
/**
|
|
2187
|
-
* 生成推荐命名
|
|
2188
|
-
* @param {string} original_name 原始名称
|
|
2189
|
-
* @param {string} long_word 过长的单词
|
|
2190
|
-
* @param {string} name_type 名称类型
|
|
2191
|
-
* @returns {string|null} 推荐命名或null
|
|
2192
|
-
*/
|
|
2193
|
-
Detector.prototype._generateRecommendedName = function (
|
|
2194
|
-
original_name,
|
|
2195
|
-
long_word,
|
|
2196
|
-
name_type,
|
|
2197
|
-
) {
|
|
2198
|
-
try {
|
|
2199
|
-
// 获取推荐词映射
|
|
2200
|
-
var recommended_words = this.config.getRecommendedWords(name_type);
|
|
2201
|
-
if (!recommended_words || Object.keys(recommended_words).length === 0) {
|
|
2202
|
-
return null;
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
|
-
var long_word_lower = long_word.toLowerCase();
|
|
2206
|
-
var is_private = original_name.startsWith("_");
|
|
2207
|
-
var original_name_without_prefix = is_private
|
|
2208
|
-
? original_name.substring(1)
|
|
2209
|
-
: original_name;
|
|
2210
|
-
|
|
2211
|
-
// 检查推荐词映射中是否有对应的缩写
|
|
2212
|
-
for (var short_word in recommended_words) {
|
|
2213
|
-
if (recommended_words.hasOwnProperty(short_word)) {
|
|
2214
|
-
var original_words = recommended_words[short_word];
|
|
2215
|
-
|
|
2216
|
-
// 检查过长的单词是否在推荐词的原词列表中
|
|
2217
|
-
for (var i = 0; i < original_words.length; i++) {
|
|
2218
|
-
var original_word = original_words[i].toLowerCase();
|
|
2219
|
-
if (long_word_lower === original_word) {
|
|
2220
|
-
// 生成推荐命名:将原始名称中的长单词替换为推荐的缩写,保持私有前缀
|
|
2221
|
-
var recommended_name_without_prefix =
|
|
2222
|
-
original_name_without_prefix.replace(long_word, short_word);
|
|
2223
|
-
var recommended_name = is_private
|
|
2224
|
-
? "_" + recommended_name_without_prefix
|
|
2225
|
-
: recommended_name_without_prefix;
|
|
2226
|
-
return recommended_name;
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
return null;
|
|
2233
|
-
} catch (error) {
|
|
2234
|
-
console.error("生成推荐命名时出错:", error);
|
|
2235
|
-
return null;
|
|
2236
|
-
}
|
|
2237
|
-
};
|
|
2238
|
-
|
|
2239
|
-
/**
|
|
2240
|
-
* 转换命名风格
|
|
2241
|
-
* @param {string} name 原始名称
|
|
2242
|
-
* @param {string} target_style 目标命名风格
|
|
2243
|
-
* @param {string} name_type 名称类型
|
|
2244
|
-
* @returns {string} 转换后的名称
|
|
2245
|
-
*/
|
|
2246
|
-
Detector.prototype._convertNamingStyle = function (
|
|
2247
|
-
name,
|
|
2248
|
-
target_style,
|
|
2249
|
-
name_type,
|
|
2250
|
-
) {
|
|
2251
|
-
try {
|
|
2252
|
-
if (!name || !target_style) {
|
|
2253
|
-
return name;
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
|
-
// 处理私有标识符
|
|
2257
|
-
var has_private_prefix = name.startsWith("_");
|
|
2258
|
-
var processed_name = has_private_prefix ? name.substring(1) : name;
|
|
2259
|
-
|
|
2260
|
-
// 分割单词
|
|
2261
|
-
var words = this._splitNameIntoWords(processed_name);
|
|
2262
|
-
|
|
2263
|
-
// 过滤空字符串和私有标识符
|
|
2264
|
-
words = words.filter(function (word) {
|
|
2265
|
-
return word && word.length > 0 && word !== "_";
|
|
2266
|
-
});
|
|
2267
|
-
|
|
2268
|
-
if (words.length === 0) {
|
|
2269
|
-
return name;
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
|
-
var converted_name = "";
|
|
2273
|
-
|
|
2274
|
-
// 根据目标风格转换
|
|
2275
|
-
switch (target_style) {
|
|
2276
|
-
case "camelCase":
|
|
2277
|
-
// 小驼峰命名法
|
|
2278
|
-
converted_name = words
|
|
2279
|
-
.map(function (word, index) {
|
|
2280
|
-
if (index === 0) {
|
|
2281
|
-
return word.toLowerCase();
|
|
2282
|
-
}
|
|
2283
|
-
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
2284
|
-
})
|
|
2285
|
-
.join("");
|
|
2286
|
-
break;
|
|
2287
|
-
|
|
2288
|
-
case "_camelCase":
|
|
2289
|
-
// 私有小驼峰命名法
|
|
2290
|
-
converted_name =
|
|
2291
|
-
"_" +
|
|
2292
|
-
words
|
|
2293
|
-
.map(function (word, index) {
|
|
2294
|
-
if (index === 0) {
|
|
2295
|
-
return word.toLowerCase();
|
|
2296
|
-
}
|
|
2297
|
-
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
2298
|
-
})
|
|
2299
|
-
.join("");
|
|
2300
|
-
break;
|
|
2301
|
-
|
|
2302
|
-
case "PascalCase":
|
|
2303
|
-
// 大驼峰命名法
|
|
2304
|
-
converted_name = words
|
|
2305
|
-
.map(function (word) {
|
|
2306
|
-
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
2307
|
-
})
|
|
2308
|
-
.join("");
|
|
2309
|
-
break;
|
|
2310
|
-
|
|
2311
|
-
case "snake_case":
|
|
2312
|
-
// 小写蛇形命名法
|
|
2313
|
-
converted_name = words
|
|
2314
|
-
.map(function (word) {
|
|
2315
|
-
return word.toLowerCase();
|
|
2316
|
-
})
|
|
2317
|
-
.join("_");
|
|
2318
|
-
break;
|
|
2319
|
-
|
|
2320
|
-
case "_snake_case":
|
|
2321
|
-
// 私有小写蛇形命名法
|
|
2322
|
-
converted_name =
|
|
2323
|
-
"_" +
|
|
2324
|
-
words
|
|
2325
|
-
.map(function (word) {
|
|
2326
|
-
return word.toLowerCase();
|
|
2327
|
-
})
|
|
2328
|
-
.join("_");
|
|
2329
|
-
break;
|
|
2330
|
-
|
|
2331
|
-
case "UPPER_SNAKE_CASE":
|
|
2332
|
-
// 大写蛇形命名法
|
|
2333
|
-
converted_name = words
|
|
2334
|
-
.map(function (word) {
|
|
2335
|
-
return word.toUpperCase();
|
|
2336
|
-
})
|
|
2337
|
-
.join("_");
|
|
2338
|
-
break;
|
|
2339
|
-
|
|
2340
|
-
default:
|
|
2341
|
-
converted_name = name;
|
|
2342
|
-
}
|
|
2343
|
-
|
|
2344
|
-
// 如果原始名称有私有前缀但目标风格不是私有风格,则添加私有前缀
|
|
2345
|
-
if (
|
|
2346
|
-
has_private_prefix &&
|
|
2347
|
-
!target_style.startsWith("_") &&
|
|
2348
|
-
!converted_name.startsWith("_")
|
|
2349
|
-
) {
|
|
2350
|
-
converted_name = "_" + converted_name;
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
return converted_name;
|
|
2354
|
-
} catch (error) {
|
|
2355
|
-
console.error("转换命名风格时出错:", error);
|
|
2356
|
-
return name;
|
|
2357
|
-
}
|
|
2358
|
-
};
|
|
2359
|
-
|
|
2360
|
-
/**
|
|
2361
|
-
* 获取命名修复建议
|
|
2362
|
-
* @param {string} name 原始名称
|
|
2363
|
-
* @param {string} name_type 名称类型
|
|
2364
|
-
* @param {string} error_message 错误信息
|
|
2365
|
-
* @returns {object|null} 修复建议对象
|
|
2366
|
-
*/
|
|
2367
|
-
Detector.prototype._getFixSuggestion = function (
|
|
2368
|
-
name,
|
|
2369
|
-
name_type,
|
|
2370
|
-
error_message,
|
|
2371
|
-
) {
|
|
2372
|
-
try {
|
|
2373
|
-
var rule = this.config.getRule(name_type);
|
|
2374
|
-
if (!rule || !rule.styles || rule.styles.length === 0) {
|
|
2375
|
-
return null;
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
// 根据名称类型智能选择目标风格
|
|
2379
|
-
var target_style = this._getSmartTargetStyle(name, name_type, rule.styles);
|
|
2380
|
-
|
|
2381
|
-
// 生成修复后的名称
|
|
2382
|
-
var fixed_name = this._convertNamingStyle(name, target_style, name_type);
|
|
2383
|
-
|
|
2384
|
-
// 优先检查错误消息中是否有建议使用的名称
|
|
2385
|
-
if (error_message && error_message.includes("建议使用:")) {
|
|
2386
|
-
// 从错误消息中提取建议的名称
|
|
2387
|
-
var suggestion_match = error_message.match(/建议使用:\s*([\w_]+)/);
|
|
2388
|
-
if (suggestion_match && suggestion_match[1]) {
|
|
2389
|
-
fixed_name = suggestion_match[1];
|
|
2390
|
-
|
|
2391
|
-
// 对于私有方法(以_开头),根据错误消息确定正确的命名风格
|
|
2392
|
-
if (name_type === "method-name" && fixed_name.startsWith("_")) {
|
|
2393
|
-
// 检查错误消息中是否指定了命名风格
|
|
2394
|
-
if (error_message.includes("_camelCase")) {
|
|
2395
|
-
target_style = "_camelCase";
|
|
2396
|
-
} else if (error_message.includes("_snake_case")) {
|
|
2397
|
-
target_style = "_snake_case";
|
|
2398
|
-
} else {
|
|
2399
|
-
// 默认使用_snake_case风格
|
|
2400
|
-
target_style = "_snake_case";
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
|
-
// 验证建议的名称是否有效
|
|
2405
|
-
var suggestion_validation = this._validate_naming_style(
|
|
2406
|
-
fixed_name,
|
|
2407
|
-
[target_style],
|
|
2408
|
-
name_type,
|
|
2409
|
-
);
|
|
2410
|
-
|
|
2411
|
-
if (suggestion_validation) {
|
|
2412
|
-
// 建议的名称无效,回退到原始转换逻辑
|
|
2413
|
-
fixed_name = this._convertNamingStyle(name, target_style, name_type);
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
// 检查是否需要智能缩写(长度过长的问题)
|
|
2419
|
-
if (error_message && error_message.includes("长度过长")) {
|
|
2420
|
-
// 如果错误消息中已经有建议名称,使用建议名称
|
|
2421
|
-
if (error_message.includes("建议使用:")) {
|
|
2422
|
-
// 已经处理过建议名称,直接使用
|
|
2423
|
-
} else {
|
|
2424
|
-
// 如果没有建议名称,使用智能缩写
|
|
2425
|
-
fixed_name = this._getSmartAbbreviation(name, name_type);
|
|
2426
|
-
|
|
2427
|
-
// 如果智能缩写后名称仍然相同,则不提供修复建议
|
|
2428
|
-
if (fixed_name === name) {
|
|
2429
|
-
return null;
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
// 支持多次缩写直到满足长度要求
|
|
2433
|
-
var max_length = rule.max || 20;
|
|
2434
|
-
var current_name = fixed_name;
|
|
2435
|
-
var max_attempts = 5; // 最多尝试5次
|
|
2436
|
-
|
|
2437
|
-
for (var attempt = 0; attempt < max_attempts; attempt++) {
|
|
2438
|
-
if (current_name.length <= max_length) {
|
|
2439
|
-
break; // 长度满足要求,退出循环
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
|
-
var next_name = this._getSmartAbbreviation(current_name, name_type);
|
|
2443
|
-
if (next_name === current_name) {
|
|
2444
|
-
break; // 无法进一步缩写,退出循环
|
|
2445
|
-
}
|
|
2446
|
-
current_name = next_name;
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
fixed_name = current_name;
|
|
2450
|
-
}
|
|
2451
|
-
} else if (fixed_name === name) {
|
|
2452
|
-
// 其他情况下,转换后名称相同,不提供修复建议
|
|
2453
|
-
return null;
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
// 检查修复后的名称是否仍然有错误
|
|
2457
|
-
var fixed_style_err = this._validate_naming_style(
|
|
2458
|
-
fixed_name,
|
|
2459
|
-
[target_style],
|
|
2460
|
-
name_type,
|
|
2461
|
-
);
|
|
2462
|
-
if (fixed_style_err) {
|
|
2463
|
-
// 修复后的名称仍然有错误,尝试其他风格
|
|
2464
|
-
for (var i = 1; i < rule.styles.length; i++) {
|
|
2465
|
-
var alternative_style = rule.styles[i];
|
|
2466
|
-
var alternative_name = this._convertNamingStyle(
|
|
2467
|
-
name,
|
|
2468
|
-
alternative_style,
|
|
2469
|
-
name_type,
|
|
2470
|
-
);
|
|
2471
|
-
var alternative_err = this._validate_naming_style(
|
|
2472
|
-
alternative_name,
|
|
2473
|
-
[alternative_style],
|
|
2474
|
-
name_type,
|
|
2475
|
-
);
|
|
2476
|
-
|
|
2477
|
-
if (!alternative_err) {
|
|
2478
|
-
fixed_name = alternative_name;
|
|
2479
|
-
target_style = alternative_style;
|
|
2480
|
-
break;
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
// 如果名称长度仍然过长,且错误消息中没有建议名称,进行智能缩写处理
|
|
2486
|
-
if (
|
|
2487
|
-
error_message &&
|
|
2488
|
-
error_message.includes("长度过长") &&
|
|
2489
|
-
!error_message.includes("建议使用:")
|
|
2490
|
-
) {
|
|
2491
|
-
fixed_name = this._getSmartAbbreviation(name, name_type);
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
return {
|
|
2495
|
-
fixed_name: fixed_name,
|
|
2496
|
-
target_style: target_style,
|
|
2497
|
-
};
|
|
2498
|
-
} catch (error) {
|
|
2499
|
-
console.error("获取命名修复建议时出错:", error);
|
|
2500
|
-
return null;
|
|
2501
|
-
}
|
|
2502
|
-
};
|
|
2503
|
-
|
|
2504
|
-
/**
|
|
2505
|
-
* 智能缩写处理(针对长度过长的名称)
|
|
2506
|
-
* @param {string} name 原始名称
|
|
2507
|
-
* @param {string} name_type 名称类型
|
|
2508
|
-
* @returns {string} 缩写后的名称
|
|
2509
|
-
*/
|
|
2510
|
-
Detector.prototype._getSmartAbbreviation = function (name, name_type) {
|
|
2511
|
-
try {
|
|
2512
|
-
// 处理私有标识符
|
|
2513
|
-
var has_private_prefix = name.startsWith("_");
|
|
2514
|
-
var processed_name = has_private_prefix ? name.substring(1) : name;
|
|
2515
|
-
|
|
2516
|
-
// 分割单词
|
|
2517
|
-
var words = this._splitNameIntoWords(processed_name);
|
|
2518
|
-
|
|
2519
|
-
// 过滤空字符串
|
|
2520
|
-
words = words.filter(function (word) {
|
|
2521
|
-
return word && word.length > 0;
|
|
2522
|
-
});
|
|
2523
|
-
|
|
2524
|
-
if (words.length === 0) {
|
|
2525
|
-
return name;
|
|
2526
|
-
}
|
|
2527
|
-
|
|
2528
|
-
// 获取配置中的推荐词映射
|
|
2529
|
-
var rec_words = {};
|
|
2530
|
-
if (
|
|
2531
|
-
this.config.getRecommendedWords &&
|
|
2532
|
-
typeof this.config.getRecommendedWords === "function"
|
|
2533
|
-
) {
|
|
2534
|
-
rec_words = this.config.getRecommendedWords(name_type);
|
|
2535
|
-
}
|
|
2536
|
-
|
|
2537
|
-
// 获取配置中的禁止词列表(作为禁止拼接词)
|
|
2538
|
-
var forbidden_words = [];
|
|
2539
|
-
if (
|
|
2540
|
-
this.config.getForbiddenWords &&
|
|
2541
|
-
typeof this.config.getForbiddenWords === "function"
|
|
2542
|
-
) {
|
|
2543
|
-
forbidden_words = this.config.getForbiddenWords(name_type);
|
|
2544
|
-
}
|
|
2545
|
-
|
|
2546
|
-
if (!rec_words || Object.keys(rec_words).length === 0) {
|
|
2547
|
-
return name;
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
// 处理每个单词,使用推荐词替换
|
|
2551
|
-
var processed_words = [];
|
|
2552
|
-
for (var i = 0; i < words.length; i++) {
|
|
2553
|
-
var word = words[i];
|
|
2554
|
-
var word_lower = word.toLowerCase();
|
|
2555
|
-
var replaced = false;
|
|
2556
|
-
|
|
2557
|
-
// 检查是否需要替换为推荐词
|
|
2558
|
-
for (var rec_word in rec_words) {
|
|
2559
|
-
if (rec_words.hasOwnProperty(rec_word)) {
|
|
2560
|
-
var original_words = rec_words[rec_word];
|
|
2561
|
-
|
|
2562
|
-
for (var j = 0; j < original_words.length; j++) {
|
|
2563
|
-
var original_word = original_words[j].toLowerCase();
|
|
2564
|
-
|
|
2565
|
-
// 如果当前单词包含原词(不区分大小写),则替换为推荐词
|
|
2566
|
-
if (word_lower.includes(original_word)) {
|
|
2567
|
-
// 保持首字母大小写
|
|
2568
|
-
if (word[0] === word[0].toUpperCase()) {
|
|
2569
|
-
processed_words.push(
|
|
2570
|
-
rec_word[0].toUpperCase() + rec_word.substring(1),
|
|
2571
|
-
);
|
|
2572
|
-
} else {
|
|
2573
|
-
processed_words.push(rec_word);
|
|
2574
|
-
}
|
|
2575
|
-
replaced = true;
|
|
2576
|
-
break;
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
if (replaced) break;
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
|
|
2584
|
-
// 如果没有找到推荐词,检查是否为禁止拼接词
|
|
2585
|
-
if (!replaced) {
|
|
2586
|
-
var is_forbidden = false;
|
|
2587
|
-
for (var f = 0; f < forbidden_words.length; f++) {
|
|
2588
|
-
var forbidden_word = forbidden_words[f].toLowerCase();
|
|
2589
|
-
if (word_lower === forbidden_word) {
|
|
2590
|
-
is_forbidden = true;
|
|
2591
|
-
break;
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
// 如果不是禁止拼接词,保留原词
|
|
2596
|
-
if (!is_forbidden) {
|
|
2597
|
-
processed_words.push(word);
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
|
|
2602
|
-
// 根据名称类型确定目标风格
|
|
2603
|
-
var target_style = "camelCase";
|
|
2604
|
-
if (name_type === "constant-name") {
|
|
2605
|
-
target_style = "UPPER_SNAKE_CASE";
|
|
2606
|
-
} else if (
|
|
2607
|
-
name_type === "class-name" ||
|
|
2608
|
-
name_type === "property-class-name"
|
|
2609
|
-
) {
|
|
2610
|
-
target_style = "PascalCase";
|
|
2611
|
-
} else if (
|
|
2612
|
-
name_type === "variable-name" ||
|
|
2613
|
-
name_type === "property-value-name"
|
|
2614
|
-
) {
|
|
2615
|
-
target_style = "snake_case";
|
|
2616
|
-
}
|
|
2617
|
-
// 函数名、方法名等保持默认的 camelCase
|
|
2618
|
-
|
|
2619
|
-
var abbreviated_name = "";
|
|
2620
|
-
|
|
2621
|
-
switch (target_style) {
|
|
2622
|
-
case "camelCase":
|
|
2623
|
-
for (var k = 0; k < processed_words.length; k++) {
|
|
2624
|
-
if (k === 0) {
|
|
2625
|
-
abbreviated_name += processed_words[k].toLowerCase();
|
|
2626
|
-
} else {
|
|
2627
|
-
abbreviated_name +=
|
|
2628
|
-
processed_words[k][0].toUpperCase() +
|
|
2629
|
-
processed_words[k].substring(1).toLowerCase();
|
|
2630
|
-
}
|
|
2631
|
-
}
|
|
2632
|
-
break;
|
|
2633
|
-
case "PascalCase":
|
|
2634
|
-
for (var l = 0; l < processed_words.length; l++) {
|
|
2635
|
-
abbreviated_name +=
|
|
2636
|
-
processed_words[l][0].toUpperCase() +
|
|
2637
|
-
processed_words[l].substring(1).toLowerCase();
|
|
2638
|
-
}
|
|
2639
|
-
break;
|
|
2640
|
-
case "snake_case":
|
|
2641
|
-
abbreviated_name = processed_words.join("_").toLowerCase();
|
|
2642
|
-
break;
|
|
2643
|
-
case "UPPER_SNAKE_CASE":
|
|
2644
|
-
abbreviated_name = processed_words.join("_").toUpperCase();
|
|
2645
|
-
break;
|
|
2646
|
-
default:
|
|
2647
|
-
abbreviated_name = processed_words.join("");
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
// 恢复私有标识符
|
|
2651
|
-
if (has_private_prefix) {
|
|
2652
|
-
abbreviated_name = "_" + abbreviated_name;
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
// 检查长度:如果缩写后长度没有变短,尝试其他策略
|
|
2656
|
-
if (abbreviated_name.length >= name.length) {
|
|
2657
|
-
return name;
|
|
2658
|
-
}
|
|
2659
|
-
|
|
2660
|
-
// 如果缩写后仍然超过最大长度,尝试进一步优化
|
|
2661
|
-
var max_length = 20;
|
|
2662
|
-
if (abbreviated_name.length > max_length) {
|
|
2663
|
-
// 策略1: 截断过长的单词
|
|
2664
|
-
var optimized_words = [];
|
|
2665
|
-
for (var k = 0; k < processed_words.length; k++) {
|
|
2666
|
-
var word = processed_words[k];
|
|
2667
|
-
if (word.length > 8) {
|
|
2668
|
-
// 截断过长的单词到8个字符
|
|
2669
|
-
optimized_words.push(word.substring(0, 8));
|
|
2670
|
-
} else {
|
|
2671
|
-
optimized_words.push(word);
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
|
-
// 重新生成名称
|
|
2676
|
-
var optimized_name = "";
|
|
2677
|
-
switch (target_style) {
|
|
2678
|
-
case "camelCase":
|
|
2679
|
-
for (var m = 0; m < optimized_words.length; m++) {
|
|
2680
|
-
if (m === 0) {
|
|
2681
|
-
optimized_name += optimized_words[m].toLowerCase();
|
|
2682
|
-
} else {
|
|
2683
|
-
optimized_name +=
|
|
2684
|
-
optimized_words[m][0].toUpperCase() +
|
|
2685
|
-
optimized_words[m].substring(1).toLowerCase();
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
break;
|
|
2689
|
-
case "PascalCase":
|
|
2690
|
-
for (var n = 0; n < optimized_words.length; n++) {
|
|
2691
|
-
optimized_name +=
|
|
2692
|
-
optimized_words[n][0].toUpperCase() +
|
|
2693
|
-
optimized_words[n].substring(1).toLowerCase();
|
|
2694
|
-
}
|
|
2695
|
-
break;
|
|
2696
|
-
case "snake_case":
|
|
2697
|
-
optimized_name = optimized_words.join("_").toLowerCase();
|
|
2698
|
-
break;
|
|
2699
|
-
case "UPPER_SNAKE_CASE":
|
|
2700
|
-
optimized_name = optimized_words.join("_").toUpperCase();
|
|
2701
|
-
break;
|
|
2702
|
-
default:
|
|
2703
|
-
optimized_name = optimized_words.join("");
|
|
2704
|
-
}
|
|
2705
|
-
|
|
2706
|
-
// 恢复私有标识符
|
|
2707
|
-
if (has_private_prefix) {
|
|
2708
|
-
optimized_name = "_" + optimized_name;
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
|
-
// 如果优化后名称更短,使用优化后的名称
|
|
2712
|
-
if (optimized_name.length < abbreviated_name.length) {
|
|
2713
|
-
abbreviated_name = optimized_name;
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
|
-
// 如果仍然超过长度,尝试移除不重要的单词
|
|
2717
|
-
if (abbreviated_name.length > max_length && processed_words.length > 2) {
|
|
2718
|
-
// 保留前2个和后1个单词
|
|
2719
|
-
var important_words = [
|
|
2720
|
-
processed_words[0],
|
|
2721
|
-
processed_words[1],
|
|
2722
|
-
processed_words[processed_words.length - 1],
|
|
2723
|
-
];
|
|
2724
|
-
|
|
2725
|
-
// 根据目标风格生成简化名称
|
|
2726
|
-
var simplified_name = "";
|
|
2727
|
-
switch (target_style) {
|
|
2728
|
-
case "camelCase":
|
|
2729
|
-
for (var o = 0; o < important_words.length; o++) {
|
|
2730
|
-
if (o === 0) {
|
|
2731
|
-
simplified_name += important_words[o].toLowerCase();
|
|
2732
|
-
} else {
|
|
2733
|
-
simplified_name +=
|
|
2734
|
-
important_words[o][0].toUpperCase() +
|
|
2735
|
-
important_words[o].substring(1).toLowerCase();
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
break;
|
|
2739
|
-
case "PascalCase":
|
|
2740
|
-
for (var p = 0; p < important_words.length; p++) {
|
|
2741
|
-
simplified_name +=
|
|
2742
|
-
important_words[p][0].toUpperCase() +
|
|
2743
|
-
important_words[p].substring(1).toLowerCase();
|
|
2744
|
-
}
|
|
2745
|
-
break;
|
|
2746
|
-
case "snake_case":
|
|
2747
|
-
simplified_name = important_words.join("_").toLowerCase();
|
|
2748
|
-
break;
|
|
2749
|
-
case "UPPER_SNAKE_CASE":
|
|
2750
|
-
simplified_name = important_words.join("_").toUpperCase();
|
|
2751
|
-
break;
|
|
2752
|
-
default:
|
|
2753
|
-
simplified_name = important_words.join("");
|
|
2754
|
-
}
|
|
2755
|
-
|
|
2756
|
-
// 恢复私有标识符
|
|
2757
|
-
if (has_private_prefix) {
|
|
2758
|
-
simplified_name = "_" + simplified_name;
|
|
2759
|
-
}
|
|
2760
|
-
|
|
2761
|
-
// 如果简化后名称更短且不超过最大长度,使用简化后的名称
|
|
2762
|
-
if (
|
|
2763
|
-
simplified_name.length <= max_length &&
|
|
2764
|
-
simplified_name.length < abbreviated_name.length
|
|
2765
|
-
) {
|
|
2766
|
-
abbreviated_name = simplified_name;
|
|
2767
|
-
}
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
|
|
2771
|
-
return abbreviated_name;
|
|
2772
|
-
} catch (error) {
|
|
2773
|
-
console.error("智能缩写处理时出错:", error);
|
|
2774
|
-
return name;
|
|
2775
|
-
}
|
|
2776
|
-
};
|
|
2777
|
-
|
|
2778
|
-
/**
|
|
2779
|
-
* 智能选择目标命名风格
|
|
2780
|
-
* @param {string} name 原始名称
|
|
2781
|
-
* @param {string} name_type 名称类型
|
|
2782
|
-
* @param {array} available_styles 可用的命名风格
|
|
2783
|
-
* @returns {string} 目标命名风格
|
|
2784
|
-
*/
|
|
2785
|
-
Detector.prototype._getSmartTargetStyle = function (
|
|
2786
|
-
name,
|
|
2787
|
-
name_type,
|
|
2788
|
-
available_styles,
|
|
2789
|
-
) {
|
|
2790
|
-
try {
|
|
2791
|
-
// 默认使用第一个风格
|
|
2792
|
-
var target_style = available_styles[0];
|
|
2793
|
-
|
|
2794
|
-
// 根据名称类型和原始名称特征智能选择
|
|
2795
|
-
switch (name_type) {
|
|
2796
|
-
case "property-value-name":
|
|
2797
|
-
// 属性值:根据是否为私有属性和值类型选择风格
|
|
2798
|
-
if (name.startsWith("_")) {
|
|
2799
|
-
// 私有属性值:优先使用私有蛇形命名
|
|
2800
|
-
target_style =
|
|
2801
|
-
available_styles.find(function (style) {
|
|
2802
|
-
return style === "_snake_case";
|
|
2803
|
-
}) || target_style;
|
|
2804
|
-
} else {
|
|
2805
|
-
// 公开属性值:优先使用蛇形命名
|
|
2806
|
-
target_style =
|
|
2807
|
-
available_styles.find(function (style) {
|
|
2808
|
-
return style === "snake_case";
|
|
2809
|
-
}) || target_style;
|
|
2810
|
-
}
|
|
2811
|
-
break;
|
|
2812
|
-
|
|
2813
|
-
case "property-class-name":
|
|
2814
|
-
// 属性类:根据是否为私有类选择风格
|
|
2815
|
-
if (name.startsWith("_")) {
|
|
2816
|
-
// 私有属性类:优先使用私有大驼峰命名
|
|
2817
|
-
target_style =
|
|
2818
|
-
available_styles.find(function (style) {
|
|
2819
|
-
return style === "_PascalCase";
|
|
2820
|
-
}) || target_style;
|
|
2821
|
-
} else {
|
|
2822
|
-
// 公开属性类:优先使用大驼峰命名
|
|
2823
|
-
target_style =
|
|
2824
|
-
available_styles.find(function (style) {
|
|
2825
|
-
return style === "PascalCase";
|
|
2826
|
-
}) || target_style;
|
|
2827
|
-
}
|
|
2828
|
-
break;
|
|
2829
|
-
|
|
2830
|
-
case "property-instance-class-name":
|
|
2831
|
-
case "property-method-name":
|
|
2832
|
-
// 属性实例类和属性方法:根据是否为私有选择风格
|
|
2833
|
-
if (name.startsWith("_")) {
|
|
2834
|
-
// 私有实例类/方法:优先使用私有小驼峰命名
|
|
2835
|
-
target_style =
|
|
2836
|
-
available_styles.find(function (style) {
|
|
2837
|
-
return style === "_camelCase";
|
|
2838
|
-
}) || target_style;
|
|
2839
|
-
} else {
|
|
2840
|
-
// 公开实例类/方法:优先使用小驼峰命名
|
|
2841
|
-
target_style =
|
|
2842
|
-
available_styles.find(function (style) {
|
|
2843
|
-
return style === "camelCase";
|
|
2844
|
-
}) || target_style;
|
|
2845
|
-
}
|
|
2846
|
-
break;
|
|
2847
|
-
|
|
2848
|
-
default:
|
|
2849
|
-
// 其他类型:使用默认风格
|
|
2850
|
-
target_style = available_styles[0];
|
|
2851
|
-
}
|
|
2852
|
-
|
|
2853
|
-
return target_style;
|
|
2854
|
-
} catch (error) {
|
|
2855
|
-
console.error("智能选择目标命名风格时出错:", error);
|
|
2856
|
-
return available_styles[0];
|
|
2857
|
-
}
|
|
2858
|
-
};
|
|
2859
|
-
|
|
2860
|
-
/**
|
|
2861
|
-
* 检测解构赋值中的常量名
|
|
2862
|
-
* @param {string} constant_name 常量名
|
|
2863
|
-
* @param {object} node AST节点
|
|
2864
|
-
* @returns {object|null} 错误信息或null
|
|
2865
|
-
*/
|
|
2866
|
-
Detector.prototype._checkConstantNameForDestructuring = function (
|
|
2867
|
-
constant_name,
|
|
2868
|
-
node,
|
|
2869
|
-
) {
|
|
2870
|
-
try {
|
|
2871
|
-
// 解构赋值中的变量应该使用变量规则,而不是常量规则
|
|
2872
|
-
var rule = this.config.getRule("variable-name");
|
|
2873
|
-
if (!rule) {
|
|
2874
|
-
return null;
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
|
-
// 检查是否为忽略词
|
|
2878
|
-
if (this._isIgnoredWord(constant_name, "variable-name")) {
|
|
2879
|
-
return null;
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
// 检查是否为解构导入(从模块导入的变量)
|
|
2883
|
-
var is_destructured_import = this._isDestructuredImport(node);
|
|
2884
|
-
|
|
2885
|
-
// 如果是解构导入,则不进行变量检测
|
|
2886
|
-
if (is_destructured_import) {
|
|
2887
|
-
return null;
|
|
2888
|
-
}
|
|
2889
|
-
|
|
2890
|
-
// 检查是否为函数赋值(函数表达式或箭头函数)
|
|
2891
|
-
var is_function_assignment = this._isFunctionAssignment(node);
|
|
2892
|
-
|
|
2893
|
-
// 如果是函数赋值,允许camelCase命名风格
|
|
2894
|
-
var allowed_styles = [];
|
|
2895
|
-
if (is_function_assignment) {
|
|
2896
|
-
allowed_styles = ["camelCase", "UPPER_SNAKE_CASE"];
|
|
2897
|
-
} else {
|
|
2898
|
-
allowed_styles = rule.styles;
|
|
2899
|
-
}
|
|
2900
|
-
|
|
2901
|
-
// 检查命名风格
|
|
2902
|
-
var style_err = this._validate_naming_style(
|
|
2903
|
-
constant_name,
|
|
2904
|
-
allowed_styles,
|
|
2905
|
-
"variable-name",
|
|
2906
|
-
);
|
|
2907
|
-
if (style_err) {
|
|
2908
|
-
return {
|
|
2909
|
-
node: node,
|
|
2910
|
-
message: style_err,
|
|
2911
|
-
};
|
|
2912
|
-
}
|
|
2913
|
-
|
|
2914
|
-
// 检查并推荐更合适的词汇(作为命名风格检查的补充)
|
|
2915
|
-
var recommend_info = this._checkAndRecommend(
|
|
2916
|
-
constant_name,
|
|
2917
|
-
"variable-name",
|
|
2918
|
-
);
|
|
2919
|
-
if (recommend_info) {
|
|
2920
|
-
// 如果有推荐建议,在错误消息中提示可以使用更简洁的词汇
|
|
2921
|
-
return {
|
|
2922
|
-
node: node,
|
|
2923
|
-
message:
|
|
2924
|
-
"变量名'" +
|
|
2925
|
-
constant_name +
|
|
2926
|
-
"'长度过长,最大长度为20字符,建议使用: " +
|
|
2927
|
-
recommend_info.recommended_name,
|
|
2928
|
-
};
|
|
2929
|
-
}
|
|
2930
|
-
|
|
2931
|
-
// 检查长度
|
|
2932
|
-
var len_err = this._checkNameLength(
|
|
2933
|
-
constant_name,
|
|
2934
|
-
rule.min,
|
|
2935
|
-
rule.max,
|
|
2936
|
-
"variable-name",
|
|
2937
|
-
);
|
|
2938
|
-
if (len_err) {
|
|
2939
|
-
return {
|
|
2940
|
-
node: node,
|
|
2941
|
-
message: len_err,
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
|
|
2945
|
-
// 检查单词长度
|
|
2946
|
-
var word_err = this._checkWordLength(
|
|
2947
|
-
constant_name,
|
|
2948
|
-
rule.single_word_len,
|
|
2949
|
-
"constant-name",
|
|
2950
|
-
);
|
|
2951
|
-
if (word_err) {
|
|
2952
|
-
return {
|
|
2953
|
-
node: node,
|
|
2954
|
-
message: word_err,
|
|
2955
|
-
};
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
|
-
// 检查禁止词汇
|
|
2959
|
-
var forbid_err = this._checkForbiddenWords(constant_name, "constant-name");
|
|
2960
|
-
if (forbid_err) {
|
|
2961
|
-
return {
|
|
2962
|
-
node: node,
|
|
2963
|
-
message: forbid_err,
|
|
2964
|
-
};
|
|
2965
|
-
}
|
|
2966
|
-
|
|
2967
|
-
return null;
|
|
2968
|
-
} catch (error) {
|
|
2969
|
-
console.error("检测解构赋值常量名时出错:", error);
|
|
2970
|
-
return null;
|
|
2971
|
-
}
|
|
2972
|
-
};
|
|
2973
|
-
|
|
2974
|
-
/**
|
|
2975
|
-
* 检查属性值命名风格
|
|
2976
|
-
* @param {string} prop_name 属性名
|
|
2977
|
-
* @param {object} val_node 属性值节点
|
|
2978
|
-
* @param {string} rule_type 规则类型
|
|
2979
|
-
* @returns {string|null} 错误信息或null
|
|
2980
|
-
*/
|
|
2981
|
-
Detector.prototype._checkPropertyValueStyle = function (
|
|
2982
|
-
prop_name,
|
|
2983
|
-
val_node,
|
|
2984
|
-
rule_type,
|
|
2985
|
-
) {
|
|
2986
|
-
try {
|
|
2987
|
-
// 首先检查禁止拼接词
|
|
2988
|
-
var forbid_err = this._checkForbiddenWords(prop_name, rule_type);
|
|
2989
|
-
if (forbid_err) {
|
|
2990
|
-
return forbid_err;
|
|
2991
|
-
}
|
|
2992
|
-
|
|
2993
|
-
// 检查属性值类型,智能选择命名规范
|
|
2994
|
-
var is_constant_value = this._isConstantValue(val_node);
|
|
2995
|
-
var is_private_property = prop_name.startsWith("_");
|
|
2996
|
-
|
|
2997
|
-
// 属性值应该允许满足常量或变量命名规则之一即可
|
|
2998
|
-
// 构建允许的命名风格列表
|
|
2999
|
-
var allowed_styles = [];
|
|
3000
|
-
|
|
3001
|
-
// 常量值:允许常量命名规范
|
|
3002
|
-
if (is_constant_value) {
|
|
3003
|
-
allowed_styles.push("UPPER_SNAKE_CASE");
|
|
3004
|
-
}
|
|
3005
|
-
|
|
3006
|
-
// 私有属性:允许私有变量命名规范
|
|
3007
|
-
if (is_private_property) {
|
|
3008
|
-
allowed_styles.push("_snake_case");
|
|
3009
|
-
}
|
|
3010
|
-
|
|
3011
|
-
// 普通属性值:允许变量命名规范
|
|
3012
|
-
allowed_styles.push("snake_case");
|
|
3013
|
-
|
|
3014
|
-
// 检查命名风格 - 只要满足其中一种风格即可
|
|
3015
|
-
for (var i = 0; i < allowed_styles.length; i++) {
|
|
3016
|
-
var style_check = this._validate_naming_style(
|
|
3017
|
-
prop_name,
|
|
3018
|
-
[allowed_styles[i]],
|
|
3019
|
-
rule_type,
|
|
3020
|
-
);
|
|
3021
|
-
if (!style_check) {
|
|
3022
|
-
// 当前风格符合要求,无需报错
|
|
3023
|
-
return null;
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
|
|
3027
|
-
// 所有风格都不符合,生成错误信息
|
|
3028
|
-
var style_names = allowed_styles.join("、");
|
|
3029
|
-
|
|
3030
|
-
return (
|
|
3031
|
-
"属性值名'" + prop_name + "'不符合" + style_names + "命名风格中的任何一种"
|
|
3032
|
-
);
|
|
3033
|
-
} catch (error) {
|
|
3034
|
-
console.error("检查属性值命名风格时出错:", error);
|
|
3035
|
-
return null;
|
|
3036
|
-
}
|
|
3037
|
-
};
|
|
3038
|
-
|
|
3039
|
-
/**
|
|
3040
|
-
* 检查属性方法命名风格
|
|
3041
|
-
* @param {string} prop_name 属性名
|
|
3042
|
-
* @param {object} val_node 属性值节点
|
|
3043
|
-
* @param {string} rule_type 规则类型
|
|
3044
|
-
* @returns {string|null} 错误信息或null
|
|
3045
|
-
*/
|
|
3046
|
-
Detector.prototype._checkPropertyMethodStyle = function (
|
|
3047
|
-
prop_name,
|
|
3048
|
-
val_node,
|
|
3049
|
-
rule_type,
|
|
3050
|
-
) {
|
|
3051
|
-
try {
|
|
3052
|
-
// 首先检查禁止拼接词 - 使用 method-name 规则类型
|
|
3053
|
-
var forbid_err = this._checkForbiddenWords(prop_name, "method-name");
|
|
3054
|
-
if (forbid_err) {
|
|
3055
|
-
return forbid_err;
|
|
3056
|
-
}
|
|
3057
|
-
|
|
3058
|
-
// 检查是否为私有属性方法
|
|
3059
|
-
var is_private_method = prop_name.startsWith("_");
|
|
3060
|
-
|
|
3061
|
-
// 属性方法应该允许满足公开方法或私有方法命名规则之一即可
|
|
3062
|
-
// 构建允许的命名风格列表
|
|
3063
|
-
var allowed_styles = [];
|
|
3064
|
-
|
|
3065
|
-
// 私有属性方法:允许私有方法命名规范
|
|
3066
|
-
if (is_private_method) {
|
|
3067
|
-
allowed_styles.push("_camelCase");
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
// 公开属性方法:允许公开方法命名规范
|
|
3071
|
-
allowed_styles.push("camelCase");
|
|
3072
|
-
|
|
3073
|
-
// 检查命名风格 - 只要满足其中一种风格即可
|
|
3074
|
-
for (var i = 0; i < allowed_styles.length; i++) {
|
|
3075
|
-
var style_check = this._validate_naming_style(
|
|
3076
|
-
prop_name,
|
|
3077
|
-
[allowed_styles[i]],
|
|
3078
|
-
rule_type,
|
|
3079
|
-
);
|
|
3080
|
-
if (!style_check) {
|
|
3081
|
-
// 当前风格符合要求,无需报错
|
|
3082
|
-
return null;
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
|
|
3086
|
-
// 所有风格都不符合,生成错误信息
|
|
3087
|
-
var style_names = allowed_styles.join("、");
|
|
3088
|
-
|
|
3089
|
-
return (
|
|
3090
|
-
"属性方法名'" + prop_name + "'不符合" + style_names + "命名风格中的任何一种"
|
|
3091
|
-
);
|
|
3092
|
-
} catch (error) {
|
|
3093
|
-
console.error("检查属性方法命名风格时出错:", error);
|
|
3094
|
-
return null;
|
|
3095
|
-
}
|
|
3096
|
-
};
|
|
3097
|
-
|
|
3098
|
-
/**
|
|
3099
|
-
* 检查属性实例类命名风格
|
|
3100
|
-
* @param {string} prop_name 属性名
|
|
3101
|
-
* @param {object} val_node 属性值节点
|
|
3102
|
-
* @param {string} rule_type 规则类型
|
|
3103
|
-
* @returns {string|null} 错误信息或null
|
|
3104
|
-
*/
|
|
3105
|
-
Detector.prototype._checkPropertyInstanceClassStyle = function (
|
|
3106
|
-
prop_name,
|
|
3107
|
-
val_node,
|
|
3108
|
-
rule_type,
|
|
3109
|
-
) {
|
|
3110
|
-
try {
|
|
3111
|
-
// 首先检查禁止拼接词
|
|
3112
|
-
var forbid_err = this._checkForbiddenWords(prop_name, "method-name");
|
|
3113
|
-
if (forbid_err) {
|
|
3114
|
-
return forbid_err;
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
|
-
// 检查是否为私有属性实例类
|
|
3118
|
-
var is_private_instance = prop_name.startsWith("_");
|
|
3119
|
-
|
|
3120
|
-
// 属性类实例应该允许满足公开或私有命名规则之一即可
|
|
3121
|
-
// 构建允许的命名风格列表
|
|
3122
|
-
var allowed_styles = [];
|
|
3123
|
-
|
|
3124
|
-
// 私有属性实例类:允许私有实例类命名规范
|
|
3125
|
-
if (is_private_instance) {
|
|
3126
|
-
allowed_styles.push("_camelCase");
|
|
3127
|
-
}
|
|
3128
|
-
|
|
3129
|
-
// 公开属性实例类:允许公开实例类命名规范
|
|
3130
|
-
allowed_styles.push("camelCase");
|
|
3131
|
-
|
|
3132
|
-
// 检查命名风格 - 只要满足其中一种风格即可
|
|
3133
|
-
for (var i = 0; i < allowed_styles.length; i++) {
|
|
3134
|
-
var style_check = this._validate_naming_style(
|
|
3135
|
-
prop_name,
|
|
3136
|
-
[allowed_styles[i]],
|
|
3137
|
-
rule_type,
|
|
3138
|
-
);
|
|
3139
|
-
if (!style_check) {
|
|
3140
|
-
// 当前风格符合要求,无需报错
|
|
3141
|
-
return null;
|
|
3142
|
-
}
|
|
3143
|
-
}
|
|
3144
|
-
|
|
3145
|
-
// 所有风格都不符合,生成错误信息
|
|
3146
|
-
var style_names = allowed_styles.join("、");
|
|
3147
|
-
|
|
3148
|
-
return (
|
|
3149
|
-
"属性实例类名'" +
|
|
3150
|
-
prop_name +
|
|
3151
|
-
"'不符合" +
|
|
3152
|
-
style_names +
|
|
3153
|
-
"命名风格中的任何一种"
|
|
3154
|
-
);
|
|
3155
|
-
} catch (error) {
|
|
3156
|
-
console.error("检查属性实例类命名风格时出错:", error);
|
|
3157
|
-
return null;
|
|
3158
|
-
}
|
|
3159
|
-
};
|
|
3160
|
-
|
|
3161
|
-
/**
|
|
3162
|
-
* 检查属性类命名风格
|
|
3163
|
-
* @param {string} prop_name 属性名
|
|
3164
|
-
* @param {object} val_node 属性值节点
|
|
3165
|
-
* @param {string} rule_type 规则类型
|
|
3166
|
-
* @returns {string|null} 错误信息或null
|
|
3167
|
-
*/
|
|
3168
|
-
Detector.prototype._checkPropertyClassStyle = function (
|
|
3169
|
-
prop_name,
|
|
3170
|
-
val_node,
|
|
3171
|
-
rule_type,
|
|
3172
|
-
) {
|
|
3173
|
-
try {
|
|
3174
|
-
// 首先检查禁止拼接词
|
|
3175
|
-
var forbid_err = this._checkForbiddenWords(prop_name, "class-name");
|
|
3176
|
-
if (forbid_err) {
|
|
3177
|
-
return forbid_err;
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
|
-
// 检查是否为私有属性类
|
|
3181
|
-
var is_private_class = prop_name.startsWith("_");
|
|
3182
|
-
|
|
3183
|
-
// 根据属性类类型选择合适的命名规范
|
|
3184
|
-
var allowed_styles = [];
|
|
3185
|
-
|
|
3186
|
-
if (is_private_class) {
|
|
3187
|
-
// 私有属性类:使用私有类命名规范
|
|
3188
|
-
allowed_styles = ["_PascalCase"];
|
|
3189
|
-
} else {
|
|
3190
|
-
// 公开属性类:使用类命名规范
|
|
3191
|
-
allowed_styles = ["PascalCase"];
|
|
3192
|
-
}
|
|
3193
|
-
|
|
3194
|
-
// 检查命名风格
|
|
3195
|
-
var style_err = null;
|
|
3196
|
-
for (var i = 0; i < allowed_styles.length; i++) {
|
|
3197
|
-
var style_check = this._validate_naming_style(
|
|
3198
|
-
prop_name,
|
|
3199
|
-
[allowed_styles[i]],
|
|
3200
|
-
rule_type,
|
|
3201
|
-
);
|
|
3202
|
-
if (!style_check) {
|
|
3203
|
-
// 当前风格符合要求,无需报错
|
|
3204
|
-
return null;
|
|
3205
|
-
}
|
|
3206
|
-
}
|
|
3207
|
-
|
|
3208
|
-
// 所有风格都不符合,生成错误信息
|
|
3209
|
-
var style_names = allowed_styles.join("、");
|
|
3210
|
-
|
|
3211
|
-
return (
|
|
3212
|
-
"属性类名'" + prop_name + "'不符合" + style_names + "命名风格中的任何一种"
|
|
3213
|
-
);
|
|
3214
|
-
} catch (error) {
|
|
3215
|
-
console.error("检查属性类命名风格时出错:", error);
|
|
3216
|
-
return null;
|
|
3217
|
-
}
|
|
3218
|
-
};
|
|
3219
|
-
|
|
3220
|
-
/**
|
|
3221
|
-
* 检查是否为常量值
|
|
3222
|
-
* @param {object} val_node 属性值节点
|
|
3223
|
-
* @returns {boolean} 是否为常量值
|
|
3224
|
-
*/
|
|
3225
|
-
Detector.prototype._isConstantValue = function (val_node) {
|
|
3226
|
-
try {
|
|
3227
|
-
if (!val_node) {
|
|
3228
|
-
return false;
|
|
3229
|
-
}
|
|
3230
|
-
|
|
3231
|
-
// 常量值类型:字面量、基础值、不可变值
|
|
3232
|
-
var is_constant = false;
|
|
3233
|
-
|
|
3234
|
-
switch (val_node.type) {
|
|
3235
|
-
case "Literal": // 字面量:数字、字符串、布尔值
|
|
3236
|
-
is_constant = true;
|
|
3237
|
-
break;
|
|
3238
|
-
case "UnaryExpression": // 一元表达式(如 -1, +100)
|
|
3239
|
-
if (val_node.operator === "-" || val_node.operator === "+") {
|
|
3240
|
-
is_constant = true;
|
|
3241
|
-
}
|
|
3242
|
-
break;
|
|
3243
|
-
case "ArrayExpression": // 数组字面量(空数组或包含常量值的数组)
|
|
3244
|
-
if (!val_node.elements || val_node.elements.length === 0) {
|
|
3245
|
-
is_constant = true;
|
|
3246
|
-
} else {
|
|
3247
|
-
// 检查数组元素是否都是常量值
|
|
3248
|
-
is_constant = val_node.elements.every(function (element) {
|
|
3249
|
-
return this._isConstantValue(element);
|
|
3250
|
-
}, this);
|
|
3251
|
-
}
|
|
3252
|
-
break;
|
|
3253
|
-
case "ObjectExpression": // 对象字面量(空对象或包含常量值的对象)
|
|
3254
|
-
if (!val_node.properties || val_node.properties.length === 0) {
|
|
3255
|
-
is_constant = true;
|
|
3256
|
-
} else {
|
|
3257
|
-
// 检查对象属性值是否都是常量值
|
|
3258
|
-
is_constant = val_node.properties.every(function (property) {
|
|
3259
|
-
return (
|
|
3260
|
-
property &&
|
|
3261
|
-
property.value &&
|
|
3262
|
-
this._isConstantValue(property.value)
|
|
3263
|
-
);
|
|
3264
|
-
}, this);
|
|
3265
|
-
}
|
|
3266
|
-
break;
|
|
3267
|
-
case "TemplateLiteral": // 模板字面量(包含常量值的模板)
|
|
3268
|
-
if (!val_node.expressions || val_node.expressions.length === 0) {
|
|
3269
|
-
is_constant = true;
|
|
3270
|
-
} else {
|
|
3271
|
-
// 检查模板表达式是否都是常量值
|
|
3272
|
-
is_constant = val_node.expressions.every(function (expression) {
|
|
3273
|
-
return this._isConstantValue(expression);
|
|
3274
|
-
}, this);
|
|
3275
|
-
}
|
|
3276
|
-
break;
|
|
3277
|
-
default:
|
|
3278
|
-
is_constant = false;
|
|
3279
|
-
}
|
|
3280
|
-
|
|
3281
|
-
return is_constant;
|
|
3282
|
-
} catch (error) {
|
|
3283
|
-
console.error("检查是否为常量值时出错:", error);
|
|
3284
|
-
return false;
|
|
3285
|
-
}
|
|
3286
|
-
};
|
|
3287
|
-
|
|
3288
|
-
/**
|
|
3289
|
-
* 检测参数声明与函数内部引用是否一致
|
|
3290
|
-
* @param {object} context ESLint上下文
|
|
3291
|
-
* @param {object} node 函数节点(FunctionDeclaration、FunctionExpression、ArrowFunctionExpression)
|
|
3292
|
-
*/
|
|
3293
|
-
Detector.prototype._checkParamReferences = function (context, node) {
|
|
3294
|
-
try {
|
|
3295
|
-
// 获取函数参数列表
|
|
3296
|
-
var params = node.params || [];
|
|
3297
|
-
if (params.length === 0) {
|
|
3298
|
-
return; // 没有参数,无需检查
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
|
-
// 提取参数名
|
|
3302
|
-
var param_names = [];
|
|
3303
|
-
for (var i = 0; i < params.length; i++) {
|
|
3304
|
-
var param = params[i];
|
|
3305
|
-
if (param.type === 'Identifier') {
|
|
3306
|
-
param_names.push(param.name);
|
|
3307
|
-
}
|
|
3308
|
-
// 处理解构参数
|
|
3309
|
-
else if (param.type === 'ObjectPattern' && param.properties) {
|
|
3310
|
-
for (var p = 0; p < param.properties.length; p++) {
|
|
3311
|
-
var property = param.properties[p];
|
|
3312
|
-
if (property.key && property.key.type === 'Identifier') {
|
|
3313
|
-
param_names.push(property.key.name);
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
}
|
|
3317
|
-
}
|
|
3318
|
-
|
|
3319
|
-
if (param_names.length === 0) {
|
|
3320
|
-
return; // 没有有效的参数名
|
|
3321
|
-
}
|
|
3322
|
-
|
|
3323
|
-
// 分析函数体内的标识符引用
|
|
3324
|
-
var body_identifiers = this._extractIdentifiers(node.body);
|
|
3325
|
-
|
|
3326
|
-
// 检查每个参数是否被正确引用
|
|
3327
|
-
for (var j = 0; j < param_names.length; j++) {
|
|
3328
|
-
var param_name = param_names[j];
|
|
3329
|
-
var is_referenced = false;
|
|
3330
|
-
var reference_errors = [];
|
|
3331
|
-
|
|
3332
|
-
// 检查参数是否被引用
|
|
3333
|
-
for (var k = 0; k < body_identifiers.length; k++) {
|
|
3334
|
-
var identifier = body_identifiers[k];
|
|
3335
|
-
|
|
3336
|
-
// 检查标识符名称与参数名的相似性
|
|
3337
|
-
if (this._isSimilarIdentifier(identifier.name, param_name)) {
|
|
3338
|
-
if (identifier.name === param_name) {
|
|
3339
|
-
// 完全匹配,正确引用
|
|
3340
|
-
is_referenced = true;
|
|
3341
|
-
break;
|
|
3342
|
-
} else {
|
|
3343
|
-
// 相似但不完全匹配,可能是拼写错误
|
|
3344
|
-
reference_errors.push({
|
|
3345
|
-
node: identifier.node,
|
|
3346
|
-
actual_name: identifier.name,
|
|
3347
|
-
expected_name: param_name
|
|
3348
|
-
});
|
|
3349
|
-
}
|
|
3350
|
-
}
|
|
3351
|
-
}
|
|
3352
|
-
|
|
3353
|
-
// 如果没有正确引用但有相似引用,报告错误
|
|
3354
|
-
if (!is_referenced && reference_errors.length > 0) {
|
|
3355
|
-
for (var m = 0; m < reference_errors.length; m++) {
|
|
3356
|
-
var error = reference_errors[m];
|
|
3357
|
-
|
|
3358
|
-
// 检查参数名是否符合命名规范,如果不符合,使用符合规范的名称
|
|
3359
|
-
var param_name_err = this._checkParamName(param_name, null);
|
|
3360
|
-
var recommended_name = param_name;
|
|
3361
|
-
|
|
3362
|
-
if (param_name_err) {
|
|
3363
|
-
var fix_suggestion = this._getFixSuggestion(param_name, 'param-name', param_name_err.message);
|
|
3364
|
-
if (fix_suggestion && fix_suggestion.fixed_name !== param_name) {
|
|
3365
|
-
recommended_name = fix_suggestion.fixed_name;
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3368
|
-
|
|
3369
|
-
context.report({
|
|
3370
|
-
node: error.node,
|
|
3371
|
-
message: "参数引用不一致:声明为 '" + param_name + "',但引用为 '" + error.actual_name + "',建议使用: " + recommended_name,
|
|
3372
|
-
fix: function(fixer) {
|
|
3373
|
-
return fixer.replaceText(error.node, recommended_name);
|
|
3374
|
-
}
|
|
3375
|
-
});
|
|
3376
|
-
}
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
3379
|
-
} catch (error) {
|
|
3380
|
-
console.error("检查参数引用一致性时出错:", error);
|
|
3381
|
-
}
|
|
3382
|
-
};
|
|
3383
|
-
|
|
3384
|
-
/**
|
|
3385
|
-
* 提取函数体内的所有标识符
|
|
3386
|
-
* @param {object} node AST节点
|
|
3387
|
-
* @returns {array} 标识符数组
|
|
3388
|
-
*/
|
|
3389
|
-
Detector.prototype._extractIdentifiers = function (node) {
|
|
3390
|
-
var identifiers = [];
|
|
3391
|
-
|
|
3392
|
-
if (!node) {
|
|
3393
|
-
return identifiers;
|
|
3394
|
-
}
|
|
3395
|
-
|
|
3396
|
-
var visited_nodes = new Set(); // 防止循环引用
|
|
3397
|
-
var max_depth = 100; // 最大递归深度
|
|
3398
|
-
|
|
3399
|
-
// 递归遍历AST节点,提取标识符
|
|
3400
|
-
function traverse(current_node, depth) {
|
|
3401
|
-
if (!current_node || depth > max_depth) return;
|
|
3402
|
-
|
|
3403
|
-
// 防止循环引用
|
|
3404
|
-
if (visited_nodes.has(current_node)) return;
|
|
3405
|
-
visited_nodes.add(current_node);
|
|
3406
|
-
|
|
3407
|
-
// 如果是标识符节点,添加到结果中
|
|
3408
|
-
if (current_node.type === 'Identifier') {
|
|
3409
|
-
identifiers.push({
|
|
3410
|
-
name: current_node.name,
|
|
3411
|
-
node: current_node
|
|
3412
|
-
});
|
|
3413
|
-
return;
|
|
3414
|
-
}
|
|
3415
|
-
|
|
3416
|
-
// 遍历子节点
|
|
3417
|
-
var child_nodes = [];
|
|
3418
|
-
|
|
3419
|
-
switch (current_node.type) {
|
|
3420
|
-
case 'FunctionExpression':
|
|
3421
|
-
case 'FunctionDeclaration':
|
|
3422
|
-
case 'ArrowFunctionExpression':
|
|
3423
|
-
// 跳过函数定义,避免递归到参数
|
|
3424
|
-
if (current_node.body) {
|
|
3425
|
-
traverse(current_node.body, depth + 1);
|
|
3426
|
-
}
|
|
3427
|
-
break;
|
|
3428
|
-
|
|
3429
|
-
case 'BlockStatement':
|
|
3430
|
-
child_nodes = current_node.body || [];
|
|
3431
|
-
break;
|
|
3432
|
-
|
|
3433
|
-
case 'VariableDeclaration':
|
|
3434
|
-
child_nodes = current_node.declarations || [];
|
|
3435
|
-
break;
|
|
3436
|
-
|
|
3437
|
-
case 'VariableDeclarator':
|
|
3438
|
-
if (current_node.id) traverse(current_node.id, depth + 1);
|
|
3439
|
-
if (current_node.init) traverse(current_node.init, depth + 1);
|
|
3440
|
-
break;
|
|
3441
|
-
|
|
3442
|
-
case 'ExpressionStatement':
|
|
3443
|
-
if (current_node.expression) traverse(current_node.expression, depth + 1);
|
|
3444
|
-
break;
|
|
3445
|
-
|
|
3446
|
-
case 'CallExpression':
|
|
3447
|
-
if (current_node.callee) traverse(current_node.callee, depth + 1);
|
|
3448
|
-
child_nodes = current_node.arguments || [];
|
|
3449
|
-
break;
|
|
3450
|
-
|
|
3451
|
-
case 'MemberExpression':
|
|
3452
|
-
if (current_node.object) traverse(current_node.object, depth + 1);
|
|
3453
|
-
if (current_node.property) traverse(current_node.property, depth + 1);
|
|
3454
|
-
break;
|
|
3455
|
-
|
|
3456
|
-
case 'ReturnStatement':
|
|
3457
|
-
if (current_node.argument) traverse(current_node.argument, depth + 1);
|
|
3458
|
-
break;
|
|
3459
|
-
|
|
3460
|
-
case 'BinaryExpression':
|
|
3461
|
-
case 'LogicalExpression':
|
|
3462
|
-
if (current_node.left) traverse(current_node.left, depth + 1);
|
|
3463
|
-
if (current_node.right) traverse(current_node.right, depth + 1);
|
|
3464
|
-
break;
|
|
3465
|
-
|
|
3466
|
-
case 'UnaryExpression':
|
|
3467
|
-
if (current_node.argument) traverse(current_node.argument, depth + 1);
|
|
3468
|
-
break;
|
|
3469
|
-
|
|
3470
|
-
case 'ConditionalExpression':
|
|
3471
|
-
if (current_node.test) traverse(current_node.test, depth + 1);
|
|
3472
|
-
if (current_node.consequent) traverse(current_node.consequent, depth + 1);
|
|
3473
|
-
if (current_node.alternate) traverse(current_node.alternate, depth + 1);
|
|
3474
|
-
break;
|
|
3475
|
-
|
|
3476
|
-
case 'ArrayExpression':
|
|
3477
|
-
child_nodes = current_node.elements || [];
|
|
3478
|
-
break;
|
|
3479
|
-
|
|
3480
|
-
case 'ObjectExpression':
|
|
3481
|
-
child_nodes = current_node.properties || [];
|
|
3482
|
-
break;
|
|
3483
|
-
|
|
3484
|
-
case 'Property':
|
|
3485
|
-
if (current_node.key) traverse(current_node.key, depth + 1);
|
|
3486
|
-
if (current_node.value) traverse(current_node.value, depth + 1);
|
|
3487
|
-
break;
|
|
3488
|
-
|
|
3489
|
-
case 'Literal':
|
|
3490
|
-
// 字面量,跳过
|
|
3491
|
-
break;
|
|
3492
|
-
|
|
3493
|
-
default:
|
|
3494
|
-
// 对于其他节点类型,只遍历已知的AST属性,避免无限递归
|
|
3495
|
-
var ast_props = ['body', 'declarations', 'expression', 'callee', 'arguments',
|
|
3496
|
-
'object', 'property', 'argument', 'left', 'right', 'test',
|
|
3497
|
-
'consequent', 'alternate', 'elements', 'properties', 'key', 'value'];
|
|
3498
|
-
|
|
3499
|
-
for (var i = 0; i < ast_props.length; i++) {
|
|
3500
|
-
var prop = ast_props[i];
|
|
3501
|
-
var value = current_node[prop];
|
|
3502
|
-
|
|
3503
|
-
if (Array.isArray(value)) {
|
|
3504
|
-
for (var j = 0; j < value.length; j++) {
|
|
3505
|
-
if (value[j] && typeof value[j] === 'object' && value[j].type) {
|
|
3506
|
-
traverse(value[j], depth + 1);
|
|
3507
|
-
}
|
|
3508
|
-
}
|
|
3509
|
-
} else if (value && typeof value === 'object' && value.type) {
|
|
3510
|
-
traverse(value, depth + 1);
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
break;
|
|
3514
|
-
}
|
|
3515
|
-
|
|
3516
|
-
// 遍历子节点数组
|
|
3517
|
-
for (var k = 0; k < child_nodes.length; k++) {
|
|
3518
|
-
if (child_nodes[k] && typeof child_nodes[k] === 'object' && child_nodes[k].type) {
|
|
3519
|
-
traverse(child_nodes[k], depth + 1);
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
}
|
|
3523
|
-
|
|
3524
|
-
traverse(node, 0);
|
|
3525
|
-
return identifiers;
|
|
3526
|
-
};
|
|
3527
|
-
|
|
3528
|
-
/**
|
|
3529
|
-
* 检查两个标识符是否相似(用于检测拼写错误)
|
|
3530
|
-
* @param {string} actual_name 实际使用的名称
|
|
3531
|
-
* @param {string} expected_name 期望的名称
|
|
3532
|
-
* @returns {boolean} 是否相似
|
|
3533
|
-
*/
|
|
3534
|
-
Detector.prototype._isSimilarIdentifier = function (actual_name, expected_name) {
|
|
3535
|
-
if (actual_name === expected_name) {
|
|
3536
|
-
return true;
|
|
3537
|
-
}
|
|
3538
|
-
|
|
3539
|
-
// 检查大小写不一致的情况
|
|
3540
|
-
if (actual_name.toLowerCase() === expected_name.toLowerCase()) {
|
|
3541
1287
|
return true;
|
|
3542
1288
|
}
|
|
1289
|
+
}
|
|
3543
1290
|
|
|
3544
|
-
|
|
3545
|
-
var common_patterns = [
|
|
3546
|
-
// 下划线相关错误
|
|
3547
|
-
{ pattern: /_/g, replacement: '' }, // 忘记下划线
|
|
3548
|
-
{ pattern: /([a-z])([A-Z])/g, replacement: '$1_$2' }, // 驼峰转蛇形
|
|
3549
|
-
{ pattern: /_([a-z])/g, replacement: function(match, p1) { return p1.toUpperCase(); } } // 蛇形转驼峰
|
|
3550
|
-
];
|
|
3551
|
-
|
|
3552
|
-
for (var i = 0; i < common_patterns.length; i++) {
|
|
3553
|
-
var pattern = common_patterns[i];
|
|
3554
|
-
var transformed_actual = actual_name.replace(pattern.pattern, pattern.replacement);
|
|
3555
|
-
var transformed_expected = expected_name.replace(pattern.pattern, pattern.replacement);
|
|
3556
|
-
|
|
3557
|
-
if (transformed_actual === transformed_expected ||
|
|
3558
|
-
transformed_actual.toLowerCase() === transformed_expected.toLowerCase()) {
|
|
3559
|
-
return true;
|
|
3560
|
-
}
|
|
3561
|
-
}
|
|
3562
|
-
|
|
3563
|
-
// 检查编辑距离(简单的相似度检查)
|
|
3564
|
-
var distance = this._levenshteinDistance(actual_name.toLowerCase(), expected_name.toLowerCase());
|
|
3565
|
-
var max_length = Math.max(actual_name.length, expected_name.length);
|
|
3566
|
-
var similarity = 1 - (distance / max_length);
|
|
3567
|
-
|
|
3568
|
-
return similarity > 0.7; // 相似度超过70%认为相似
|
|
3569
|
-
};
|
|
3570
|
-
|
|
3571
|
-
/**
|
|
3572
|
-
* 计算两个字符串的编辑距离(Levenshtein距离)
|
|
3573
|
-
* @param {string} a 字符串A
|
|
3574
|
-
* @param {string} b 字符串B
|
|
3575
|
-
* @returns {number} 编辑距离
|
|
3576
|
-
*/
|
|
3577
|
-
Detector.prototype._levenshteinDistance = function (a, b) {
|
|
3578
|
-
if (a.length === 0) return b.length;
|
|
3579
|
-
if (b.length === 0) return a.length;
|
|
3580
|
-
|
|
3581
|
-
var matrix = [];
|
|
3582
|
-
|
|
3583
|
-
// 初始化矩阵
|
|
3584
|
-
for (var i = 0; i <= b.length; i++) {
|
|
3585
|
-
matrix[i] = [i];
|
|
3586
|
-
}
|
|
3587
|
-
|
|
3588
|
-
for (var j = 0; j <= a.length; j++) {
|
|
3589
|
-
matrix[0][j] = j;
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
// 计算编辑距离
|
|
3593
|
-
for (i = 1; i <= b.length; i++) {
|
|
3594
|
-
for (j = 1; j <= a.length; j++) {
|
|
3595
|
-
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
3596
|
-
matrix[i][j] = matrix[i - 1][j - 1];
|
|
3597
|
-
} else {
|
|
3598
|
-
matrix[i][j] = Math.min(
|
|
3599
|
-
matrix[i - 1][j - 1] + 1, // 替换
|
|
3600
|
-
matrix[i][j - 1] + 1, // 插入
|
|
3601
|
-
matrix[i - 1][j] + 1 // 删除
|
|
3602
|
-
);
|
|
3603
|
-
}
|
|
3604
|
-
}
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
return matrix[b.length][a.length];
|
|
3608
|
-
};
|
|
3609
|
-
|
|
3610
|
-
/**
|
|
3611
|
-
* 检查是否为 module.exports 中的属性
|
|
3612
|
-
* @param {object} node 属性节点
|
|
3613
|
-
* @param {object} parent_node 父节点信息
|
|
3614
|
-
* @returns {boolean} 是否为 module.exports 中的属性
|
|
3615
|
-
*/
|
|
3616
|
-
Detector.prototype._isModuleExportsProperty = function (node, parent_node) {
|
|
3617
|
-
try {
|
|
3618
|
-
// 处理新的父节点信息结构(包含node和parent属性)
|
|
3619
|
-
var actual_parent_node = parent_node;
|
|
3620
|
-
if (parent_node && typeof parent_node === "object" && parent_node.node) {
|
|
3621
|
-
actual_parent_node = parent_node.node;
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
if (!node || !actual_parent_node) {
|
|
3625
|
-
return false;
|
|
3626
|
-
}
|
|
3627
|
-
|
|
3628
|
-
// 检查父节点是否为 ObjectExpression(对象字面量)
|
|
3629
|
-
if (actual_parent_node.type === "ObjectExpression") {
|
|
3630
|
-
// 检查父节点的父节点是否为 AssignmentExpression
|
|
3631
|
-
var actual_parent_parent =
|
|
3632
|
-
parent_node && typeof parent_node === "object" && parent_node.parent
|
|
3633
|
-
? parent_node.parent
|
|
3634
|
-
: null;
|
|
3635
|
-
|
|
3636
|
-
if (
|
|
3637
|
-
actual_parent_parent &&
|
|
3638
|
-
actual_parent_parent.type === "AssignmentExpression"
|
|
3639
|
-
) {
|
|
3640
|
-
// 检查赋值表达式的左侧是否为 module.exports
|
|
3641
|
-
var left = actual_parent_parent.left;
|
|
3642
|
-
if (
|
|
3643
|
-
left &&
|
|
3644
|
-
left.type === "MemberExpression" &&
|
|
3645
|
-
left.object &&
|
|
3646
|
-
left.object.type === "Identifier" &&
|
|
3647
|
-
left.object.name === "module" &&
|
|
3648
|
-
left.property &&
|
|
3649
|
-
left.property.type === "Identifier" &&
|
|
3650
|
-
left.property.name === "exports"
|
|
3651
|
-
) {
|
|
3652
|
-
return true;
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
|
|
3657
|
-
return false;
|
|
3658
|
-
} catch (error) {
|
|
3659
|
-
console.error("检查是否为 module.exports 中的属性时出错:", error);
|
|
3660
|
-
return false;
|
|
3661
|
-
}
|
|
3662
|
-
};
|
|
3663
|
-
|
|
3664
|
-
module.exports = { Detector };
|
|
1291
|
+
module.exports = { Detector };
|