mm_eslint 1.1.8 → 1.2.0
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 +131 -34
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -205,6 +205,10 @@ class Detector {
|
|
|
205
205
|
function: [
|
|
206
206
|
"middleware", "controller", "component", "repository", "validator",
|
|
207
207
|
"transformer", "serializer", "interceptor", "decorator", "provider"
|
|
208
|
+
],
|
|
209
|
+
variable: [
|
|
210
|
+
"middleware", "controller", "component", "repository", "validator",
|
|
211
|
+
"transformer", "serializer", "interceptor", "decorator", "provider"
|
|
208
212
|
]
|
|
209
213
|
},
|
|
210
214
|
|
|
@@ -368,10 +372,7 @@ class Detector {
|
|
|
368
372
|
"snake_case",
|
|
369
373
|
"_snake_case",
|
|
370
374
|
"UPPERCASE",
|
|
371
|
-
"UPPER_SNAKE_CASE"
|
|
372
|
-
"PascalCase",
|
|
373
|
-
"camelCase",
|
|
374
|
-
"_camelCase"
|
|
375
|
+
"UPPER_SNAKE_CASE"
|
|
375
376
|
],
|
|
376
377
|
strong: true, // 强验证模式:根据属性值类型智能判断;false:弱验证模式:只需符合变量名或常量名规则
|
|
377
378
|
},
|
|
@@ -765,10 +766,11 @@ Detector.prototype._splitWords = function (name, rule_type) {
|
|
|
765
766
|
* 属性名检查方法(根据属性值类型应用不同规则)
|
|
766
767
|
* @param {string} name - 属性名
|
|
767
768
|
* @param {Object} val_node - 属性值AST节点
|
|
769
|
+
* @param {Object} parent_node - 父节点AST节点(可选,用于上下文感知检测)
|
|
768
770
|
* @returns {Object} 检查结果
|
|
769
771
|
* @private
|
|
770
772
|
*/
|
|
771
|
-
Detector.prototype._checkPropName = function (name, val_node) {
|
|
773
|
+
Detector.prototype._checkPropName = function (name, val_node, parent_node) {
|
|
772
774
|
if (!name) {
|
|
773
775
|
throw new TypeError("属性名不能为空");
|
|
774
776
|
}
|
|
@@ -785,7 +787,7 @@ Detector.prototype._checkPropName = function (name, val_node) {
|
|
|
785
787
|
this._checkBadWords(name, config, errors);
|
|
786
788
|
|
|
787
789
|
const use_strong_val = config.strong !== false;
|
|
788
|
-
const val_type = this._getPropType(val_node, name);
|
|
790
|
+
const val_type = this._getPropType(val_node, name, parent_node);
|
|
789
791
|
|
|
790
792
|
if (use_strong_val) {
|
|
791
793
|
this._checkPropStrong(name, val_type, config, errors);
|
|
@@ -830,6 +832,9 @@ Detector.prototype._checkPropStrong = function (
|
|
|
830
832
|
case "function":
|
|
831
833
|
this._checkFuncProp(name, config, errors);
|
|
832
834
|
break;
|
|
835
|
+
case "method":
|
|
836
|
+
this._checkMethodProp(name, config, errors);
|
|
837
|
+
break;
|
|
833
838
|
case "class":
|
|
834
839
|
this._checkClassProp(name, config, errors);
|
|
835
840
|
break;
|
|
@@ -852,6 +857,9 @@ Detector.prototype._checkPropWeak = function (name, val_type, config, errors) {
|
|
|
852
857
|
case "function":
|
|
853
858
|
this._checkFuncProp(name, config, errors);
|
|
854
859
|
break;
|
|
860
|
+
case "method":
|
|
861
|
+
this._checkMethodProp(name, config, errors);
|
|
862
|
+
break;
|
|
855
863
|
case "class":
|
|
856
864
|
this._checkClassProp(name, config, errors);
|
|
857
865
|
break;
|
|
@@ -899,6 +907,17 @@ Detector.prototype._checkConstProp = function (name, config, errors) {
|
|
|
899
907
|
}
|
|
900
908
|
};
|
|
901
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
|
+
|
|
902
921
|
/**
|
|
903
922
|
* 建议常量命名
|
|
904
923
|
* @private
|
|
@@ -926,21 +945,20 @@ Detector.prototype._suggestConstName = function (name, config, errors) {
|
|
|
926
945
|
* @private
|
|
927
946
|
*/
|
|
928
947
|
Detector.prototype._checkValueProp = function (name, config, errors) {
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
errors.push(
|
|
934
|
-
`${config.name}(值)禁止使用大写蛇形命名(如PRODUCT_ID或PRODUCTID)`,
|
|
935
|
-
);
|
|
948
|
+
// 检查常量规则
|
|
949
|
+
const const_res = this._checkName("constant-name", name);
|
|
950
|
+
if (const_res.valid) {
|
|
951
|
+
return; // 满足常量规则,通过
|
|
936
952
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
}
|
|
953
|
+
|
|
954
|
+
// 检查变量规则
|
|
955
|
+
const var_res = this._checkName("variable-name", name);
|
|
956
|
+
if (var_res.valid) {
|
|
957
|
+
return; // 满足变量规则,通过
|
|
943
958
|
}
|
|
959
|
+
|
|
960
|
+
// 都不满足,报告错误
|
|
961
|
+
errors.push(`${config.name}(值)必须符合变量或常量命名规则`);
|
|
944
962
|
};
|
|
945
963
|
|
|
946
964
|
/**
|
|
@@ -1005,14 +1023,22 @@ Detector.prototype._checkPropSingleWord = function (name, config, warnings) {
|
|
|
1005
1023
|
/**
|
|
1006
1024
|
* 获取属性值类型
|
|
1007
1025
|
* @param {Object} val_node - 属性值AST节点
|
|
1008
|
-
* @
|
|
1026
|
+
* @param {string} prop_name - 属性名
|
|
1027
|
+
* @param {Object} parent_node - 父节点AST节点(可选,用于上下文感知检测)
|
|
1028
|
+
* @returns {string} 属性值类型('function', 'method', 'class', 'value')
|
|
1009
1029
|
* @private
|
|
1010
1030
|
*/
|
|
1011
|
-
Detector.prototype._getPropType = function (val_node, prop_name) {
|
|
1031
|
+
Detector.prototype._getPropType = function (val_node, prop_name, parent_node) {
|
|
1012
1032
|
if (!val_node) {
|
|
1013
1033
|
return "value"; // 默认值类型
|
|
1014
1034
|
}
|
|
1015
1035
|
|
|
1036
|
+
// 检查是否为对象方法(在对象字面量中的函数)
|
|
1037
|
+
if (parent_node && parent_node.type === "Property" &&
|
|
1038
|
+
(val_node.type === "FunctionExpression" || val_node.type === "ArrowFunctionExpression")) {
|
|
1039
|
+
return "method"; // 对象方法类型
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1016
1042
|
// 检查是否为函数
|
|
1017
1043
|
if (
|
|
1018
1044
|
val_node.type === "FunctionExpression" ||
|
|
@@ -1028,14 +1054,24 @@ Detector.prototype._getPropType = function (val_node, prop_name) {
|
|
|
1028
1054
|
}
|
|
1029
1055
|
|
|
1030
1056
|
// 检查是否为类
|
|
1031
|
-
if (
|
|
1032
|
-
val_node.type === "ClassExpression" ||
|
|
1033
|
-
(val_node.type === "Identifier" &&
|
|
1034
|
-
val_node.name &&
|
|
1035
|
-
/^[A-Z][a-zA-Z]*$/.test(val_node.name))
|
|
1036
|
-
) {
|
|
1057
|
+
if (val_node.type === "ClassExpression" || val_node.type === "NewExpression") {
|
|
1037
1058
|
return "class";
|
|
1038
1059
|
}
|
|
1060
|
+
|
|
1061
|
+
// 对于标识符类型的值,采用保守策略
|
|
1062
|
+
// 只有当明显是类名时才认为是类类型
|
|
1063
|
+
if (val_node.type === "Identifier" && val_node.name) {
|
|
1064
|
+
// 检查标识符名称是否符合类名模式(PascalCase)
|
|
1065
|
+
const is_pascal_case = /^[A-Z][a-zA-Z0-9]*$/.test(val_node.name);
|
|
1066
|
+
|
|
1067
|
+
// 如果标识符名称符合PascalCase,且不是常见的配置参数名,则认为是类类型
|
|
1068
|
+
const common_config_names = ['config', 'options', 'params', 'settings', 'props'];
|
|
1069
|
+
const is_common_config = common_config_names.includes(val_node.name.toLowerCase());
|
|
1070
|
+
|
|
1071
|
+
if (is_pascal_case && !is_common_config) {
|
|
1072
|
+
return "class";
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1039
1075
|
|
|
1040
1076
|
// 检查是否为常量/配置值(应该使用UPPERCASE风格)
|
|
1041
1077
|
if (this._isConst(val_node, prop_name)) {
|
|
@@ -1075,6 +1111,58 @@ Detector.prototype._isConst = function (val_node, prop_name) {
|
|
|
1075
1111
|
return this._isConstWithoutName(is_prim_const, is_cfg_obj, is_const_id);
|
|
1076
1112
|
};
|
|
1077
1113
|
|
|
1114
|
+
/**
|
|
1115
|
+
* 判断标识符是否为类名(基于上下文分析)
|
|
1116
|
+
* @param {string} name - 标识符名称
|
|
1117
|
+
* @param {Object} context_node - 上下文AST节点
|
|
1118
|
+
* @returns {boolean} 是否为类名
|
|
1119
|
+
* @private
|
|
1120
|
+
*/
|
|
1121
|
+
Detector.prototype._isClassName = function (name, context_node) {
|
|
1122
|
+
if (!name || typeof name !== 'string') {
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// 首先检查是否为大驼峰命名(类名的基本要求)
|
|
1127
|
+
if (!/^[A-Z][a-zA-Z]*$/.test(name)) {
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// 如果有上下文节点,进行更精确的分析
|
|
1132
|
+
if (context_node) {
|
|
1133
|
+
// 检查是否在类声明中
|
|
1134
|
+
if (context_node.type === 'ClassDeclaration' ||
|
|
1135
|
+
context_node.type === 'ClassExpression') {
|
|
1136
|
+
return true;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// 检查是否在 extends 或 implements 中
|
|
1140
|
+
if (context_node.type === 'Identifier' &&
|
|
1141
|
+
context_node.parent &&
|
|
1142
|
+
(context_node.parent.type === 'ClassDeclaration' ||
|
|
1143
|
+
context_node.parent.type === 'ClassExpression')) {
|
|
1144
|
+
return true;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// 检查是否在 new 表达式中
|
|
1148
|
+
if (context_node.type === 'NewExpression' &&
|
|
1149
|
+
context_node.callee &&
|
|
1150
|
+
context_node.callee.name === name) {
|
|
1151
|
+
return true;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// 对于没有上下文的情况,采用保守策略
|
|
1156
|
+
// 只对明显是类名的标识符返回 true
|
|
1157
|
+
const class_suffixes = [
|
|
1158
|
+
'Manager', 'Controller', 'Service', 'Factory', 'Builder',
|
|
1159
|
+
'Provider', 'Middleware', 'Component', 'Repository', 'Handler',
|
|
1160
|
+
'Adapter', 'Decorator', 'Validator', 'Generator', 'Processor'
|
|
1161
|
+
];
|
|
1162
|
+
|
|
1163
|
+
return class_suffixes.some(suffix => name.endsWith(suffix));
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1078
1166
|
/**
|
|
1079
1167
|
* 检查是否为基本类型常量
|
|
1080
1168
|
* @private
|
|
@@ -1586,6 +1674,8 @@ Detector.prototype._isIgnoreWord = function (name, rule_type) {
|
|
|
1586
1674
|
ignore_list = ignore_words_config.method || [];
|
|
1587
1675
|
} else if (rule_type === "function-name") {
|
|
1588
1676
|
ignore_list = ignore_words_config.function || [];
|
|
1677
|
+
} else if (rule_type === "variable-name") {
|
|
1678
|
+
ignore_list = ignore_words_config.variable || [];
|
|
1589
1679
|
}
|
|
1590
1680
|
|
|
1591
1681
|
// 检查名称是否完全匹配忽略词列表中的任何一个词
|
|
@@ -1984,6 +2074,13 @@ const variable_name_rule = {
|
|
|
1984
2074
|
single_word: { type: "boolean" },
|
|
1985
2075
|
single_word_len: { type: "number" },
|
|
1986
2076
|
styles: { type: "array", items: { type: "string" } },
|
|
2077
|
+
ignore_words: {
|
|
2078
|
+
type: "object",
|
|
2079
|
+
properties: {
|
|
2080
|
+
variable: { type: "array", items: { type: "string" } }
|
|
2081
|
+
},
|
|
2082
|
+
additionalProperties: false
|
|
2083
|
+
}
|
|
1987
2084
|
},
|
|
1988
2085
|
additionalProperties: false,
|
|
1989
2086
|
},
|
|
@@ -2349,7 +2446,7 @@ const prop_name_rule = {
|
|
|
2349
2446
|
// 如果是真正的属性,使用属性规则检测
|
|
2350
2447
|
const prop_name = node.key.name;
|
|
2351
2448
|
const detector = new Detector({ "property-name": options });
|
|
2352
|
-
const result = detector._checkPropName(prop_name, node.value);
|
|
2449
|
+
const result = detector._checkPropName(prop_name, node.value, node);
|
|
2353
2450
|
|
|
2354
2451
|
if (!result.valid) {
|
|
2355
2452
|
result.errors.forEach((error) => {
|
|
@@ -2592,9 +2689,9 @@ const instance_property_rule = {
|
|
|
2592
2689
|
!node.left.property.name.startsWith("_")
|
|
2593
2690
|
) {
|
|
2594
2691
|
const prop_name = node.left.property.name;
|
|
2595
|
-
//
|
|
2596
|
-
const detector = new Detector({ "
|
|
2597
|
-
const result = detector.
|
|
2692
|
+
// 实例属性应该符合属性命名规范(小写蛇形)
|
|
2693
|
+
const detector = new Detector({ "property-name": options });
|
|
2694
|
+
const result = detector._checkPropName(prop_name, node.right, node);
|
|
2598
2695
|
|
|
2599
2696
|
if (!result.valid) {
|
|
2600
2697
|
result.errors.forEach((error) => {
|
|
@@ -2615,9 +2712,9 @@ const instance_property_rule = {
|
|
|
2615
2712
|
!node.key.name.startsWith("_")
|
|
2616
2713
|
) {
|
|
2617
2714
|
const prop_name = node.key.name;
|
|
2618
|
-
//
|
|
2619
|
-
const detector = new Detector({ "
|
|
2620
|
-
const result = detector.
|
|
2715
|
+
// 实例属性应该符合属性命名规范(小写蛇形)
|
|
2716
|
+
const detector = new Detector({ "property-name": options });
|
|
2717
|
+
const result = detector._checkPropName(prop_name, node.value, node);
|
|
2621
2718
|
|
|
2622
2719
|
if (!result.valid) {
|
|
2623
2720
|
result.errors.forEach((error) => {
|