mm_eslint 1.1.9 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +133 -35
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -372,10 +372,7 @@ class Detector {
|
|
|
372
372
|
"snake_case",
|
|
373
373
|
"_snake_case",
|
|
374
374
|
"UPPERCASE",
|
|
375
|
-
"UPPER_SNAKE_CASE"
|
|
376
|
-
"PascalCase",
|
|
377
|
-
"camelCase",
|
|
378
|
-
"_camelCase"
|
|
375
|
+
"UPPER_SNAKE_CASE"
|
|
379
376
|
],
|
|
380
377
|
strong: true, // 强验证模式:根据属性值类型智能判断;false:弱验证模式:只需符合变量名或常量名规则
|
|
381
378
|
},
|
|
@@ -769,10 +766,11 @@ Detector.prototype._splitWords = function (name, rule_type) {
|
|
|
769
766
|
* 属性名检查方法(根据属性值类型应用不同规则)
|
|
770
767
|
* @param {string} name - 属性名
|
|
771
768
|
* @param {Object} val_node - 属性值AST节点
|
|
769
|
+
* @param {Object} parent_node - 父节点AST节点(可选,用于上下文感知检测)
|
|
772
770
|
* @returns {Object} 检查结果
|
|
773
771
|
* @private
|
|
774
772
|
*/
|
|
775
|
-
Detector.prototype._checkPropName = function (name, val_node) {
|
|
773
|
+
Detector.prototype._checkPropName = function (name, val_node, parent_node) {
|
|
776
774
|
if (!name) {
|
|
777
775
|
throw new TypeError("属性名不能为空");
|
|
778
776
|
}
|
|
@@ -789,7 +787,7 @@ Detector.prototype._checkPropName = function (name, val_node) {
|
|
|
789
787
|
this._checkBadWords(name, config, errors);
|
|
790
788
|
|
|
791
789
|
const use_strong_val = config.strong !== false;
|
|
792
|
-
const val_type = this._getPropType(val_node, name);
|
|
790
|
+
const val_type = this._getPropType(val_node, name, parent_node);
|
|
793
791
|
|
|
794
792
|
if (use_strong_val) {
|
|
795
793
|
this._checkPropStrong(name, val_type, config, errors);
|
|
@@ -834,6 +832,9 @@ Detector.prototype._checkPropStrong = function (
|
|
|
834
832
|
case "function":
|
|
835
833
|
this._checkFuncProp(name, config, errors);
|
|
836
834
|
break;
|
|
835
|
+
case "method":
|
|
836
|
+
this._checkMethodProp(name, config, errors);
|
|
837
|
+
break;
|
|
837
838
|
case "class":
|
|
838
839
|
this._checkClassProp(name, config, errors);
|
|
839
840
|
break;
|
|
@@ -856,6 +857,9 @@ Detector.prototype._checkPropWeak = function (name, val_type, config, errors) {
|
|
|
856
857
|
case "function":
|
|
857
858
|
this._checkFuncProp(name, config, errors);
|
|
858
859
|
break;
|
|
860
|
+
case "method":
|
|
861
|
+
this._checkMethodProp(name, config, errors);
|
|
862
|
+
break;
|
|
859
863
|
case "class":
|
|
860
864
|
this._checkClassProp(name, config, errors);
|
|
861
865
|
break;
|
|
@@ -903,6 +907,17 @@ Detector.prototype._checkConstProp = function (name, config, errors) {
|
|
|
903
907
|
}
|
|
904
908
|
};
|
|
905
909
|
|
|
910
|
+
/**
|
|
911
|
+
* 检查方法属性
|
|
912
|
+
* @private
|
|
913
|
+
*/
|
|
914
|
+
Detector.prototype._checkMethodProp = function (name, config, errors) {
|
|
915
|
+
const method_res = this._checkName("method-name", name);
|
|
916
|
+
if (!method_res.valid) {
|
|
917
|
+
errors.push(`${config.name}(方法)${method_res.errors.join(", ")}`);
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
|
|
906
921
|
/**
|
|
907
922
|
* 建议常量命名
|
|
908
923
|
* @private
|
|
@@ -930,21 +945,20 @@ Detector.prototype._suggestConstName = function (name, config, errors) {
|
|
|
930
945
|
* @private
|
|
931
946
|
*/
|
|
932
947
|
Detector.prototype._checkValueProp = function (name, config, errors) {
|
|
933
|
-
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
errors.push(
|
|
938
|
-
`${config.name}(值)禁止使用大写蛇形命名(如PRODUCT_ID或PRODUCTID)`,
|
|
939
|
-
);
|
|
948
|
+
// 检查常量规则
|
|
949
|
+
const const_res = this._checkName("constant-name", name);
|
|
950
|
+
if (const_res.valid) {
|
|
951
|
+
return; // 满足常量规则,通过
|
|
940
952
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
}
|
|
953
|
+
|
|
954
|
+
// 检查变量规则
|
|
955
|
+
const var_res = this._checkName("variable-name", name);
|
|
956
|
+
if (var_res.valid) {
|
|
957
|
+
return; // 满足变量规则,通过
|
|
947
958
|
}
|
|
959
|
+
|
|
960
|
+
// 都不满足,报告错误
|
|
961
|
+
errors.push(`${config.name}(值)必须符合变量或常量命名规则`);
|
|
948
962
|
};
|
|
949
963
|
|
|
950
964
|
/**
|
|
@@ -1009,14 +1023,22 @@ Detector.prototype._checkPropSingleWord = function (name, config, warnings) {
|
|
|
1009
1023
|
/**
|
|
1010
1024
|
* 获取属性值类型
|
|
1011
1025
|
* @param {Object} val_node - 属性值AST节点
|
|
1012
|
-
* @
|
|
1026
|
+
* @param {string} prop_name - 属性名
|
|
1027
|
+
* @param {Object} parent_node - 父节点AST节点(可选,用于上下文感知检测)
|
|
1028
|
+
* @returns {string} 属性值类型('function', 'method', 'class', 'value')
|
|
1013
1029
|
* @private
|
|
1014
1030
|
*/
|
|
1015
|
-
Detector.prototype._getPropType = function (val_node, prop_name) {
|
|
1031
|
+
Detector.prototype._getPropType = function (val_node, prop_name, parent_node) {
|
|
1016
1032
|
if (!val_node) {
|
|
1017
1033
|
return "value"; // 默认值类型
|
|
1018
1034
|
}
|
|
1019
1035
|
|
|
1036
|
+
// 检查是否为对象方法(在对象字面量中的函数)
|
|
1037
|
+
if (parent_node && parent_node.type === "Property" &&
|
|
1038
|
+
(val_node.type === "FunctionExpression" || val_node.type === "ArrowFunctionExpression")) {
|
|
1039
|
+
return "method"; // 对象方法类型
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1020
1042
|
// 检查是否为函数
|
|
1021
1043
|
if (
|
|
1022
1044
|
val_node.type === "FunctionExpression" ||
|
|
@@ -1026,20 +1048,44 @@ Detector.prototype._getPropType = function (val_node, prop_name) {
|
|
|
1026
1048
|
(val_node.name.endsWith("Function") ||
|
|
1027
1049
|
val_node.name.endsWith("Func") ||
|
|
1028
1050
|
val_node.name.startsWith("on") ||
|
|
1029
|
-
val_node.name.startsWith("handle")
|
|
1051
|
+
val_node.name.startsWith("handle") ||
|
|
1052
|
+
// 新增:常见函数命名模式识别
|
|
1053
|
+
val_node.name.endsWith("Manager") ||
|
|
1054
|
+
val_node.name.endsWith("Admin") ||
|
|
1055
|
+
val_node.name.endsWith("Handler") ||
|
|
1056
|
+
val_node.name.endsWith("Service") ||
|
|
1057
|
+
val_node.name.endsWith("Util") ||
|
|
1058
|
+
val_node.name.endsWith("Helper") ||
|
|
1059
|
+
val_node.name.endsWith("Factory") ||
|
|
1060
|
+
val_node.name.endsWith("Provider") ||
|
|
1061
|
+
// 检查是否为小驼峰命名(函数常见命名风格)
|
|
1062
|
+
(/^[a-z][a-zA-Z0-9]*$/.test(val_node.name) &&
|
|
1063
|
+
val_node.name !== val_node.name.toLowerCase() &&
|
|
1064
|
+
!val_node.name.includes("_") &&
|
|
1065
|
+
!val_node.name.includes("-"))))
|
|
1030
1066
|
) {
|
|
1031
1067
|
return "function";
|
|
1032
1068
|
}
|
|
1033
1069
|
|
|
1034
1070
|
// 检查是否为类
|
|
1035
|
-
if (
|
|
1036
|
-
val_node.type === "ClassExpression" ||
|
|
1037
|
-
(val_node.type === "Identifier" &&
|
|
1038
|
-
val_node.name &&
|
|
1039
|
-
/^[A-Z][a-zA-Z]*$/.test(val_node.name))
|
|
1040
|
-
) {
|
|
1071
|
+
if (val_node.type === "ClassExpression" || val_node.type === "NewExpression") {
|
|
1041
1072
|
return "class";
|
|
1042
1073
|
}
|
|
1074
|
+
|
|
1075
|
+
// 对于标识符类型的值,采用保守策略
|
|
1076
|
+
// 只有当明显是类名时才认为是类类型
|
|
1077
|
+
if (val_node.type === "Identifier" && val_node.name) {
|
|
1078
|
+
// 检查标识符名称是否符合类名模式(PascalCase)
|
|
1079
|
+
const is_pascal_case = /^[A-Z][a-zA-Z0-9]*$/.test(val_node.name);
|
|
1080
|
+
|
|
1081
|
+
// 如果标识符名称符合PascalCase,且不是常见的配置参数名,则认为是类类型
|
|
1082
|
+
const common_config_names = ['config', 'options', 'params', 'settings', 'props'];
|
|
1083
|
+
const is_common_config = common_config_names.includes(val_node.name.toLowerCase());
|
|
1084
|
+
|
|
1085
|
+
if (is_pascal_case && !is_common_config) {
|
|
1086
|
+
return "class";
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1043
1089
|
|
|
1044
1090
|
// 检查是否为常量/配置值(应该使用UPPERCASE风格)
|
|
1045
1091
|
if (this._isConst(val_node, prop_name)) {
|
|
@@ -1079,6 +1125,58 @@ Detector.prototype._isConst = function (val_node, prop_name) {
|
|
|
1079
1125
|
return this._isConstWithoutName(is_prim_const, is_cfg_obj, is_const_id);
|
|
1080
1126
|
};
|
|
1081
1127
|
|
|
1128
|
+
/**
|
|
1129
|
+
* 判断标识符是否为类名(基于上下文分析)
|
|
1130
|
+
* @param {string} name - 标识符名称
|
|
1131
|
+
* @param {Object} context_node - 上下文AST节点
|
|
1132
|
+
* @returns {boolean} 是否为类名
|
|
1133
|
+
* @private
|
|
1134
|
+
*/
|
|
1135
|
+
Detector.prototype._isClassName = function (name, context_node) {
|
|
1136
|
+
if (!name || typeof name !== 'string') {
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// 首先检查是否为大驼峰命名(类名的基本要求)
|
|
1141
|
+
if (!/^[A-Z][a-zA-Z]*$/.test(name)) {
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// 如果有上下文节点,进行更精确的分析
|
|
1146
|
+
if (context_node) {
|
|
1147
|
+
// 检查是否在类声明中
|
|
1148
|
+
if (context_node.type === 'ClassDeclaration' ||
|
|
1149
|
+
context_node.type === 'ClassExpression') {
|
|
1150
|
+
return true;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// 检查是否在 extends 或 implements 中
|
|
1154
|
+
if (context_node.type === 'Identifier' &&
|
|
1155
|
+
context_node.parent &&
|
|
1156
|
+
(context_node.parent.type === 'ClassDeclaration' ||
|
|
1157
|
+
context_node.parent.type === 'ClassExpression')) {
|
|
1158
|
+
return true;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// 检查是否在 new 表达式中
|
|
1162
|
+
if (context_node.type === 'NewExpression' &&
|
|
1163
|
+
context_node.callee &&
|
|
1164
|
+
context_node.callee.name === name) {
|
|
1165
|
+
return true;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// 对于没有上下文的情况,采用保守策略
|
|
1170
|
+
// 只对明显是类名的标识符返回 true
|
|
1171
|
+
const class_suffixes = [
|
|
1172
|
+
'Manager', 'Controller', 'Service', 'Factory', 'Builder',
|
|
1173
|
+
'Provider', 'Middleware', 'Component', 'Repository', 'Handler',
|
|
1174
|
+
'Adapter', 'Decorator', 'Validator', 'Generator', 'Processor'
|
|
1175
|
+
];
|
|
1176
|
+
|
|
1177
|
+
return class_suffixes.some(suffix => name.endsWith(suffix));
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1082
1180
|
/**
|
|
1083
1181
|
* 检查是否为基本类型常量
|
|
1084
1182
|
* @private
|
|
@@ -2362,7 +2460,7 @@ const prop_name_rule = {
|
|
|
2362
2460
|
// 如果是真正的属性,使用属性规则检测
|
|
2363
2461
|
const prop_name = node.key.name;
|
|
2364
2462
|
const detector = new Detector({ "property-name": options });
|
|
2365
|
-
const result = detector._checkPropName(prop_name, node.value);
|
|
2463
|
+
const result = detector._checkPropName(prop_name, node.value, node);
|
|
2366
2464
|
|
|
2367
2465
|
if (!result.valid) {
|
|
2368
2466
|
result.errors.forEach((error) => {
|
|
@@ -2605,9 +2703,9 @@ const instance_property_rule = {
|
|
|
2605
2703
|
!node.left.property.name.startsWith("_")
|
|
2606
2704
|
) {
|
|
2607
2705
|
const prop_name = node.left.property.name;
|
|
2608
|
-
//
|
|
2609
|
-
const detector = new Detector({ "
|
|
2610
|
-
const result = detector.
|
|
2706
|
+
// 实例属性应该符合属性命名规范(小写蛇形)
|
|
2707
|
+
const detector = new Detector({ "property-name": options });
|
|
2708
|
+
const result = detector._checkPropName(prop_name, node.right, node);
|
|
2611
2709
|
|
|
2612
2710
|
if (!result.valid) {
|
|
2613
2711
|
result.errors.forEach((error) => {
|
|
@@ -2628,9 +2726,9 @@ const instance_property_rule = {
|
|
|
2628
2726
|
!node.key.name.startsWith("_")
|
|
2629
2727
|
) {
|
|
2630
2728
|
const prop_name = node.key.name;
|
|
2631
|
-
//
|
|
2632
|
-
const detector = new Detector({ "
|
|
2633
|
-
const result = detector.
|
|
2729
|
+
// 实例属性应该符合属性命名规范(小写蛇形)
|
|
2730
|
+
const detector = new Detector({ "property-name": options });
|
|
2731
|
+
const result = detector._checkPropName(prop_name, node.value, node);
|
|
2634
2732
|
|
|
2635
2733
|
if (!result.valid) {
|
|
2636
2734
|
result.errors.forEach((error) => {
|