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.
Files changed (2) hide show
  1. package/index.js +131 -34
  2. 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
- const is_upper_snake = /^[A-Z][A-Z0-9_]*(_[A-Z0-9]+)+$/.test(name);
930
- const is_upper_multi = /^[A-Z][A-Z0-9]*[A-Z][A-Z0-9]*$/.test(name);
931
-
932
- if (is_upper_snake || is_upper_multi) {
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
- if (config.styles && config.styles.length > 0) {
939
- const is_valid_style = this._checkStyle(name, config.styles);
940
- if (!is_valid_style && !is_upper_snake && !is_upper_multi) {
941
- this._suggestValueName(name, config, errors);
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
- * @returns {string} 属性值类型('function', 'class', 'value')
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({ "method-name": options });
2597
- const result = detector.checkName(prop_name, "method-name");
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({ "method-name": options });
2620
- const result = detector.checkName(prop_name, "method-name");
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_eslint",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
4
4
  "description": "ESLint plugin for naming conventions - PascalCase, camelCase, snake_case, and UPPER_SNAKE_CASE naming rules",
5
5
  "main": "index.js",
6
6
  "keywords": [