mm_eslint 1.0.8 → 1.1.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 (4) hide show
  1. package/README.md +91 -0
  2. package/README_EN.md +91 -0
  3. package/index.js +512 -41
  4. package/package.json +2 -2
package/README.md CHANGED
@@ -17,6 +17,7 @@
17
17
  - ✅ **单词长度限制** - 每个单词限制在最大8字符
18
18
  - ✅ **禁止废话词** - 避免使用Manager、Handler等冗余词汇
19
19
  - ✅ **JSDoc支持** - 集成JSDoc文档要求
20
+ - ✅ **命名推荐功能** - 推荐更短的替代名称,同时保持语义不变
20
21
 
21
22
  ## 安装
22
23
 
@@ -268,8 +269,98 @@ const customConfig = {
268
269
  };
269
270
 
270
271
  const detector = new NamingDetector(customConfig);
272
+
273
+ ## 命名推荐功能
274
+
275
+ 插件包含智能命名推荐系统,建议使用更短的替代名称,同时保持语义不变。
276
+
277
+ ### 核心原则
278
+
279
+ 核心原则是**"缩短命名而不改变语义"**。推荐系统:
280
+ - 推荐更短的单词,同时保持相同含义
281
+ - 避免会改变功能的概念性变化
282
+ - 确保推荐词始终比原词短
283
+ - 保留编程特定术语和概念差异
284
+
285
+ ### 配置
286
+
287
+ 在ESLint配置中启用推荐功能:
288
+
289
+ ```javascript
290
+ // eslint.config.js
291
+ const namingConventionPlugin = require("mm_eslint");
292
+
293
+ module.exports = [
294
+ {
295
+ files: ["**/*.js"],
296
+ plugins: {
297
+ "naming-convention": namingConventionPlugin,
298
+ },
299
+ rules: {
300
+ "naming-convention/class-name": "error",
301
+ "naming-convention/function-name": ["error", { recommen: true }], // 启用推荐功能
302
+ // ... 其他规则
303
+ },
304
+ },
305
+ ];
306
+ ```
307
+
308
+ ### 可用推荐
309
+
310
+ 插件为常见编程操作提供推荐:
311
+
312
+ | 原词 | 推荐词 | 描述 |
313
+ |------|--------|------|
314
+ | `calculate` | `calc` | 数学运算 |
315
+ | `generate` | `gen` | 数据生成 |
316
+ | `initialize` | `init` | 初始化操作 |
317
+ | `execute` | `exec` | 命令执行 |
318
+ | `process` | `run` | 数据处理 |
319
+ | `retrieve` | `load` | 数据加载 |
320
+ | `persist` | `save` | 数据存储 |
321
+ | `compare` | `cmp` | 比较操作 |
322
+ | `duplicate` | `copy` | 数据复制 |
323
+ | `transfer` | `move` | 数据移动 |
324
+ | `convert` | `to` | 类型转换 |
325
+ | `verify` | `check` | 验证检查 |
326
+ | `construct` | `create` | 对象创建 |
327
+ | `handle` | `run` | 事件处理 |
328
+
329
+ ### 示例
330
+
331
+ ```javascript
332
+ // ❌ 原词(较长名称)
333
+ function calculateTotal() {}
334
+ function generateUserData() {}
335
+ function initializeSystem() {}
336
+ function executeCommand() {}
337
+ function processData() {}
338
+
339
+ // ✅ 推荐词(较短名称)
340
+ function calcTotal() {}
341
+ function genUserData() {}
342
+ function initSystem() {}
343
+ function execCommand() {}
344
+ function runData() {}
345
+ ```
346
+
347
+ ### 带推荐的错误消息
348
+
349
+ 启用推荐功能后,ESLint将建议替代名称:
350
+
351
+ ```bash
352
+ # ESLint输出示例
353
+ function calculateTotal() {}
354
+ # ^ 建议使用'calc'替代'calculate'以获得更短的命名
271
355
  ```
272
356
 
357
+ ### 优势
358
+
359
+ 1. **提高可读性**:更短的名称更容易阅读和理解
360
+ 2. **一致的代码库**:项目中的标准化命名
361
+ 3. **语义保持**:在减少冗长的同时保持含义
362
+ 4. **编程最佳实践**:遵循简洁命名的行业标准
363
+
273
364
  ## 开发
274
365
 
275
366
  ### 项目结构
package/README_EN.md CHANGED
@@ -17,6 +17,7 @@ An ESLint plugin based on personal naming conventions, supporting naming convent
17
17
  - ✅ **Word Length Limit** - Each word limited to maximum 8 characters
18
18
  - ✅ **Forbidden Words** - Avoids redundant words like Manager, Handler, etc.
19
19
  - ✅ **JSDoc Support** - Integrated JSDoc documentation requirements
20
+ - ✅ **Naming Recommendations** - Recommends shorter alternative names while preserving semantic meaning
20
21
 
21
22
  ## Installation
22
23
 
@@ -268,8 +269,98 @@ const customConfig = {
268
269
  };
269
270
 
270
271
  const detector = new NamingDetector(customConfig);
272
+
273
+ ## Naming Recommendation Feature
274
+
275
+ The plugin includes an intelligent naming recommendation system that suggests shorter alternative names while preserving semantic meaning.
276
+
277
+ ### Principle
278
+
279
+ The core principle is **"shorten names without changing semantics"**. The recommendation system:
280
+ - Recommends shorter words that maintain the same meaning
281
+ - Avoids conceptual changes that would alter functionality
282
+ - Ensures recommended words are always shorter than the original
283
+ - Preserves programming-specific terminology and conceptual differences
284
+
285
+ ### Configuration
286
+
287
+ Enable the recommendation feature in your ESLint configuration:
288
+
289
+ ```javascript
290
+ // eslint.config.js
291
+ const namingConventionPlugin = require("mm-eslint-plugin");
292
+
293
+ module.exports = [
294
+ {
295
+ files: ["**/*.js"],
296
+ plugins: {
297
+ "naming-convention": namingConventionPlugin,
298
+ },
299
+ rules: {
300
+ "naming-convention/class-name": "error",
301
+ "naming-convention/function-name": ["error", { recommen: true }], // Enable recommendations
302
+ // ... other rules
303
+ },
304
+ },
305
+ ];
306
+ ```
307
+
308
+ ### Available Recommendations
309
+
310
+ The plugin provides recommendations for common programming operations:
311
+
312
+ | Original Word | Recommended Word | Description |
313
+ |---------------|------------------|-------------|
314
+ | `calculate` | `calc` | Mathematical operations |
315
+ | `generate` | `gen` | Data generation |
316
+ | `initialize` | `init` | Initialization operations |
317
+ | `execute` | `exec` | Command execution |
318
+ | `process` | `run` | Data processing |
319
+ | `retrieve` | `load` | Data loading |
320
+ | `persist` | `save` | Data storage |
321
+ | `compare` | `cmp` | Comparison operations |
322
+ | `duplicate` | `copy` | Data copying |
323
+ | `transfer` | `move` | Data movement |
324
+ | `convert` | `to` | Type conversion |
325
+ | `verify` | `check` | Validation checks |
326
+ | `construct` | `create` | Object creation |
327
+ | `handle` | `run` | Event handling |
328
+
329
+ ### Examples
330
+
331
+ ```javascript
332
+ // ❌ Original (longer names)
333
+ function calculateTotal() {}
334
+ function generateUserData() {}
335
+ function initializeSystem() {}
336
+ function executeCommand() {}
337
+ function processData() {}
338
+
339
+ // ✅ Recommended (shorter names)
340
+ function calcTotal() {}
341
+ function genUserData() {}
342
+ function initSystem() {}
343
+ function execCommand() {}
344
+ function runData() {}
345
+ ```
346
+
347
+ ### Error Messages with Recommendations
348
+
349
+ When the recommendation feature is enabled, ESLint will suggest alternative names:
350
+
351
+ ```bash
352
+ # ESLint output example
353
+ function calculateTotal() {}
354
+ # ^ Recommend using 'calc' instead of 'calculate' for shorter naming
271
355
  ```
272
356
 
357
+ ### Benefits
358
+
359
+ 1. **Improved Readability**: Shorter names are easier to read and understand
360
+ 2. **Consistent Codebase**: Standardized naming across the project
361
+ 3. **Semantic Preservation**: Meaning is maintained while reducing verbosity
362
+ 4. **Programming Best Practices**: Follows industry standards for concise naming
363
+
273
364
  ## Development
274
365
 
275
366
  ### Project Structure
package/index.js CHANGED
@@ -5,19 +5,239 @@
5
5
  */
6
6
  class Detector {
7
7
  static config = {
8
- forbidden_words: [
9
- "manager",
10
- "handler",
11
- "processor",
12
- "controller",
13
- "Management",
14
- "Handler",
15
- "Processor",
16
- "Controller",
17
- "LoggerManager",
18
- "AppState",
19
- "UserHandler",
20
- ],
8
+ // 推荐词功能开关
9
+ recommen: true,
10
+ // 推荐词,当命名时较长时推荐使用的词
11
+ recommen_words: {
12
+ // 函数推荐词
13
+ function: {
14
+ // === 动词类 ===
15
+ // 增加
16
+ add: {
17
+ words: ["push", "insert"]
18
+ },
19
+ // 删除
20
+ del: {
21
+ words: ["remove", "delete"]
22
+ },
23
+ // 查询
24
+ get: {
25
+ words: ["query"]
26
+ },
27
+ // 修改
28
+ set: {
29
+ words: ["setting"]
30
+ },
31
+ // 转换
32
+ to: {
33
+ words: ["convert", "transform"]
34
+ },
35
+ // 检查
36
+ check: {
37
+ words: ["verify", "validate", "confirm"]
38
+ },
39
+ // 创建
40
+ create: {
41
+ words: ["construct"]
42
+ },
43
+ // 生成
44
+ gen: {
45
+ words: ["generate"]
46
+ },
47
+ // 初始化
48
+ init: {
49
+ words: ["initialize"]
50
+ },
51
+ // 处理
52
+ run: {
53
+ words: ["process", "handle"]
54
+ },
55
+ // 执行
56
+ exec: {
57
+ words: ["execute"]
58
+ },
59
+ // 计算
60
+ calc: {
61
+ words: ["calculate", "compute"]
62
+ },
63
+ // 加载
64
+ load: {
65
+ words: ["fetch", "retrieve"]
66
+ },
67
+ // 保存
68
+ save: {
69
+ words: ["store", "persist"]
70
+ },
71
+ // 比较
72
+ cmp: {
73
+ words: ["compare", "diff"]
74
+ },
75
+ // 排序
76
+ sort: {
77
+ words: ["order", "arrange"]
78
+ },
79
+ // 查找
80
+ find: {
81
+ words: ["search", "locate"]
82
+ },
83
+ // 删除
84
+ del: {
85
+ words: ["remove", "delete"]
86
+ },
87
+ // 解析
88
+ parse: {
89
+ words: ["analyze"]
90
+ },
91
+ // 复制
92
+ copy: {
93
+ words: ["duplicate", "clone"]
94
+ },
95
+ // 移动
96
+ move: {
97
+ words: ["transfer", "relocate"]
98
+ },
99
+ // 压缩
100
+ zip: {
101
+ words: ["compress"]
102
+ },
103
+ // 解压
104
+ unzip: {
105
+ words: ["decompress"]
106
+ },
107
+ // 反转
108
+ flip: {
109
+ words: ["reverse"]
110
+ },
111
+ // === 名词类 ===
112
+ // 函数
113
+ Func: {
114
+ words: ["Function"]
115
+ },
116
+ // 值
117
+ Val: {
118
+ words: ["Value"]
119
+ },
120
+ // 数组
121
+ Arr: {
122
+ words: ["Array"]
123
+ },
124
+ // 对象
125
+ Obj: {
126
+ words: ["Object"]
127
+ },
128
+ // 数值
129
+ Num: {
130
+ words: ["Number"]
131
+ },
132
+ // 字符串
133
+ Str: {
134
+ words: ["String"]
135
+ },
136
+ // 字典
137
+ Dict: {
138
+ words: ["Dictionary"]
139
+ }
140
+ },
141
+ variable: {
142
+ // 连接器
143
+ conn: {
144
+ words: ["connector"]
145
+ },
146
+ // 函数
147
+ func: {
148
+ words: ["function"]
149
+ },
150
+ // 值
151
+ val: {
152
+ words: ["value"]
153
+ },
154
+ // 枚举
155
+ enum: {
156
+ words: ["enumeration"]
157
+ },
158
+ // 数组
159
+ arr: {
160
+ words: ["array"]
161
+ },
162
+ // 对象
163
+ obj: {
164
+ words: ["object"]
165
+ },
166
+ // 数值
167
+ num: {
168
+ words: ["number"]
169
+ },
170
+ // 字符串
171
+ str: {
172
+ words: ["string"]
173
+ },
174
+ bool: {
175
+ words: ["boolean"]
176
+ },
177
+ // 字典
178
+ dict: {
179
+ words: ["dictionary"]
180
+ },
181
+ // 模块
182
+ mod: {
183
+ words: ["module"]
184
+ }
185
+ }
186
+ },
187
+ // 废话词
188
+ forbidden_words: {
189
+ // 类废话词:禁止在类名中使用的冗余拼接词汇(统一小写)
190
+ // 这些词汇在类名中属于过度设计或冗余描述,即使在某些场景下合理,但作为拼接命名仍然是冗余的
191
+ class: [
192
+ "manager", "handler", "processor", "controller", "service", "provider",
193
+ "factory", "builder", "adapter", "decorator", "proxy", "facade",
194
+ "mediator", "observer", "strategy", "command", "visitor",
195
+ "iterator", "template", "flyweight", "memento", "interpreter",
196
+ "model", "view", "route", "router", "component", "widget", "panel",
197
+ "dialog", "window", "form", "field", "property", "entity"
198
+ // 例如:UserModel → User (直接使用User),UserController → User (直接使用User)
199
+ ],
200
+
201
+ // 函数废话词:禁止在函数名中使用的冗余拼接词汇(统一小写)
202
+ // 基于动词+名词组合分析:如果动词已经表达了动作,名词只是数据描述,则名词是废话词
203
+ // 有意义的词通常是拼接get、set、to后能有意义、直接表达的名词,如getUser、toJSON等
204
+ function: [
205
+ // 无意义的数据描述词汇(动词已经表达了动作,这些名词是冗余的)
206
+ "data", "result", "output", "input", "param", "params", "parameter", "parameters",
207
+ "value", "values", "item", "items",
208
+ // 通用处理词汇(动词已经表达了处理,这些名词是冗余的)
209
+ "process", "handler", "processor", "manager", "controller", "service", "provider",
210
+ "factory", "builder", "adapter", "decorator", "proxy", "facade", "mediator",
211
+ "observer", "strategy", "command", "visitor", "iterator", "template",
212
+ "flyweight", "memento", "interpreter",
213
+ // 临时缓存词汇(动词已经表达了操作,这些名词是冗余的)
214
+ "temp", "tmp", "temporary", "cached", "buffered",
215
+ // 计数索引词汇(动词已经表达了计数索引,这些名词是冗余的)
216
+ "idx", "counter",
217
+ // 例如:getData → get(data是冗余),toResult → to(result是冗余)
218
+ // processData → process(data是冗余),handleRequest → handle(request是冗余)
219
+ ],
220
+
221
+ // 变量废话词:禁止在变量名中使用的冗余拼接词汇(统一小写)
222
+ // 这些词汇在变量名中属于"不用说也知道"的过度描述
223
+ variable: [
224
+ // 数据类型词:变量本身就有类型,不需要重复说明
225
+ "object", "array", "map", "set", "collection", "container", "instance",
226
+ // 通用数据词:变量本身就是数据/值,不需要重复说明
227
+ "value", "data", "item", "items", "element", "elements", "entry", "entries",
228
+ // 临时状态词:临时变量不需要说明是临时的
229
+ "temp", "tmp", "temporary", "cached", "buffer", "buffered",
230
+ // 输入输出词:变量本身就有用途,不需要重复说明
231
+ "input", "output", "result", "source", "target", "destination",
232
+ // 计数索引词:变量本身就有计数/索引功能,不需要重复说明
233
+ "count", "index", "idx", "counter", "length", "size", "total", "sum",
234
+ // 引用指针词:变量本身就是引用/指针,不需要重复说明
235
+ "pointer", "reference", "ref", "handle", "handler",
236
+ // 通用描述词:变量本身就是实体,不需要重复说明
237
+ "entity", "record", "row", "column", "cell", "field", "property", "attr", "attribute"
238
+ // 例如:user_list → users(list是冗余),temp_value → temp(value是冗余)
239
+ ]
240
+ },
21
241
  regex: {
22
242
  PascalCase: /^[A-Z][a-zA-Z]*$/, // 大驼峰:大写字母开头,只包含字母。主要用于类名
23
243
  camelCase: /^[a-z][a-zA-Z]*$/, // 小驼峰:小写字母开头,只包含字母。主要用于方法名、函数名
@@ -159,8 +379,10 @@ class Detector {
159
379
  // 合并默认配置和传入配置
160
380
  const merged_cfg = { ...Detector.config };
161
381
  if (config && typeof config === "object") {
382
+ // 先合并顶层配置
162
383
  Object.keys(config).forEach((key) => {
163
- if (merged_cfg[key]) {
384
+ if (merged_cfg[key] && typeof merged_cfg[key] === "object" && !Array.isArray(merged_cfg[key])) {
385
+ // 如果是对象配置(如规则配置),则深度合并
164
386
  merged_cfg[key] = { ...merged_cfg[key], ...config[key] };
165
387
 
166
388
  // 处理字符串形式的正则表达式
@@ -183,6 +405,9 @@ class Detector {
183
405
  merged_cfg[key].regex = null;
184
406
  }
185
407
  }
408
+ } else {
409
+ // 如果是顶层字段(如recommen),直接覆盖
410
+ merged_cfg[key] = config[key];
186
411
  }
187
412
  });
188
413
  }
@@ -250,17 +475,20 @@ Detector.prototype._checkName = function (rule_type, name) {
250
475
 
251
476
  const errors = [];
252
477
  const warnings = [];
478
+ const recommendations = [];
253
479
 
254
480
  this._checkNameLength(name, config, errors);
255
481
  this._checkNameFormat(name, rule_type, config, errors);
256
482
  this._checkBadWords(name, config, errors);
257
483
  this._checkSingleWord(name, rule_type, config, warnings);
258
484
  this._checkWordLength(name, rule_type, config, errors);
485
+ this._checkRecommen(name, rule_type, config, recommendations);
259
486
 
260
487
  return {
261
488
  valid: errors.length === 0,
262
489
  errors: errors,
263
490
  warnings: warnings,
491
+ recommendations: recommendations,
264
492
  };
265
493
  };
266
494
 
@@ -317,7 +545,16 @@ Detector.prototype._checkNameFormat = function (
317
545
  * @private
318
546
  */
319
547
  Detector.prototype._checkBadWords = function (name, config, errors) {
320
- if (this._hasBadWord(name)) {
548
+ // 根据配置类型确定代码实体类型
549
+ let type = 'variable'; // 默认变量类型
550
+
551
+ if (config.name.includes('类名')) {
552
+ type = 'class';
553
+ } else if (config.name.includes('函数名') || config.name.includes('方法名')) {
554
+ type = 'function';
555
+ }
556
+
557
+ if (this._hasBadWord(name, type)) {
321
558
  errors.push(`${config.name}包含禁止的废话词`);
322
559
  }
323
560
  };
@@ -1173,23 +1410,145 @@ Detector.prototype._isConstPat = function (prop_name, value) {
1173
1410
  };
1174
1411
 
1175
1412
  /**
1176
- * 检查是否包含禁止的废话词
1413
+ * 检查是否包含禁止的废话词(拼接命名检测)
1177
1414
  * @param {string} name - 名称
1415
+ * @param {string} type - 代码实体类型(class/function/variable)
1178
1416
  * @returns {boolean} 是否包含禁止词
1179
1417
  * @private
1180
1418
  */
1181
- Detector.prototype._hasBadWord = function (name) {
1182
- if (!name) {
1419
+ Detector.prototype._hasBadWord = function (name, type) {
1420
+ if (!name || !type) {
1183
1421
  return false;
1184
1422
  }
1185
1423
 
1186
- const forbidden_words = this.config.forbidden_words || [];
1424
+ const forbidden_words = this.config.forbidden_words[type] || [];
1425
+ const nameLower = name.toLowerCase();
1426
+
1427
+ // 检测拼接命名:名称由多个单词组成,且包含禁止词
1428
+ // 例如:UserManager(包含manager)、DataHandler(包含handler)
1429
+ // 但允许单个单词:Manager、Handler等
1430
+
1431
+ // 如果名称是单个单词,不进行废话词检测
1432
+ if (!this._isCompoundName(name)) {
1433
+ return false;
1434
+ }
1187
1435
 
1188
1436
  return forbidden_words.some((word) =>
1189
- name.toLowerCase().includes(word.toLowerCase()),
1437
+ nameLower.includes(word.toLowerCase()),
1190
1438
  );
1191
1439
  };
1192
1440
 
1441
+ /**
1442
+ * 检查是否为拼接命名(由多个单词组成)
1443
+ * @param {string} name - 名称
1444
+ * @returns {boolean} 是否为拼接命名
1445
+ * @private
1446
+ */
1447
+ Detector.prototype._isCompoundName = function (name) {
1448
+ if (!name) {
1449
+ return false;
1450
+ }
1451
+
1452
+ // 检测大驼峰命名(PascalCase):每个单词首字母大写
1453
+ if (/^[A-Z][a-z]+([A-Z][a-z]+)+$/.test(name)) {
1454
+ return true;
1455
+ }
1456
+
1457
+ // 检测小驼峰命名(camelCase):第一个单词小写,后续单词首字母大写
1458
+ if (/^[a-z]+([A-Z][a-z]+)+$/.test(name)) {
1459
+ return true;
1460
+ }
1461
+
1462
+ // 检测蛇形命名(snake_case):单词间以下划线分隔
1463
+ if (/^[a-z]+(_[a-z]+)+$/.test(name)) {
1464
+ return true;
1465
+ }
1466
+
1467
+ // 检测横杠命名(kebab-case):单词间以短横线分隔
1468
+ if (/^[a-z]+(-[a-z]+)+$/.test(name)) {
1469
+ return true;
1470
+ }
1471
+
1472
+ return false;
1473
+ };
1474
+
1475
+ /**
1476
+ * 检查推荐词并生成推荐建议
1477
+ * @param {string} name - 名称
1478
+ * @param {string} rule_type - 规则类型
1479
+ * @param {Object} config - 配置
1480
+ * @param {Array} recommendations - 推荐建议数组
1481
+ * @private
1482
+ */
1483
+ Detector.prototype._checkRecommen = function (name, rule_type, config, recommendations) {
1484
+ // 只对函数和方法名进行推荐词检测
1485
+ if (rule_type !== "function-name" && rule_type !== "method-name") {
1486
+ return;
1487
+ }
1488
+
1489
+ const recommen_words = this.config.recommen_words.function;
1490
+
1491
+ // 拆分命名中的单词
1492
+ const words = this._splitWords(name, rule_type);
1493
+
1494
+ // 检查每个推荐词类别
1495
+ Object.keys(recommen_words).forEach((category) => {
1496
+ const category_config = recommen_words[category];
1497
+ const category_words = category_config.words || [];
1498
+
1499
+ // 检查拆分后的单词是否包含推荐词类别对应的推荐词
1500
+ let has_target_word = false;
1501
+ words.forEach((word) => {
1502
+ if (category_words.includes(word)) {
1503
+ // 如果单词是目标词(如remove、delete),则生成建议
1504
+ has_target_word = true;
1505
+ }
1506
+ });
1507
+
1508
+ // 如果包含目标词,则生成建议
1509
+ if (has_target_word) {
1510
+ // 检查是否需要生成推荐
1511
+ let should_recommend = false;
1512
+
1513
+ if (this.config.recommen) {
1514
+ // recommen为true:所有符合条件的命名都应该推荐
1515
+ should_recommend = true;
1516
+ } else {
1517
+ // recommen为false:只有当命名中出现单个词汇长度超过指定字符或命名总长度超过限制时才推荐
1518
+ const has_long_word = words.some(word => word.length > (config.single_word_len || 8));
1519
+ const has_long_name = name.length > config.max;
1520
+
1521
+ should_recommend = has_long_word || has_long_name;
1522
+ }
1523
+
1524
+ if (should_recommend) {
1525
+ // 生成推荐建议:将目标词替换为推荐类别词
1526
+ const suggestions = category_words.map((word) => {
1527
+ // 智能替换:保持原始大小写格式
1528
+ const regex = new RegExp(word, 'gi');
1529
+ return name.replace(regex, (match) => {
1530
+ // 保持原始大小写格式
1531
+ if (match === match.toUpperCase()) {
1532
+ return category.toUpperCase();
1533
+ } else if (match[0] === match[0].toUpperCase()) {
1534
+ return category[0].toUpperCase() + category.slice(1);
1535
+ } else {
1536
+ return category;
1537
+ }
1538
+ });
1539
+ });
1540
+
1541
+ recommendations.push({
1542
+ original: name,
1543
+ category: category,
1544
+ suggestions: suggestions,
1545
+ message: `检测到"${category_words.join('、')}",推荐使用: ${suggestions.join(', ')}`
1546
+ });
1547
+ }
1548
+ }
1549
+ });
1550
+ };
1551
+
1193
1552
  /**
1194
1553
  * 检测名称是否符合规范
1195
1554
  * @param {string} name - 名称
@@ -1256,7 +1615,7 @@ const class_name_rule = {
1256
1615
  docs: {
1257
1616
  description: "类名必须使用大驼峰命名法(PascalCase)且优先使用单个单词",
1258
1617
  category: "Stylistic Issues",
1259
- recommended: true,
1618
+ recommen: true,
1260
1619
  },
1261
1620
  schema: [
1262
1621
  {
@@ -1304,7 +1663,7 @@ const function_name_rule = {
1304
1663
  docs: {
1305
1664
  description: "函数名必须使用小驼峰命名法(camelCase)且优先使用单个单词",
1306
1665
  category: "Stylistic Issues",
1307
- recommended: true,
1666
+ recommen: true,
1308
1667
  },
1309
1668
  schema: [
1310
1669
  {
@@ -1371,7 +1730,7 @@ const method_name_rule = {
1371
1730
  docs: {
1372
1731
  description: "方法名必须使用小驼峰命名法(camelCase)且优先使用单个单词",
1373
1732
  category: "Stylistic Issues",
1374
- recommended: true,
1733
+ recommen: true,
1375
1734
  },
1376
1735
  schema: [
1377
1736
  {
@@ -1436,7 +1795,7 @@ const variable_name_rule = {
1436
1795
  docs: {
1437
1796
  description: "变量名必须使用小写蛇形命名法(snake_case)",
1438
1797
  category: "Stylistic Issues",
1439
- recommended: true,
1798
+ recommen: true,
1440
1799
  },
1441
1800
  schema: [
1442
1801
  {
@@ -1460,7 +1819,8 @@ const variable_name_rule = {
1460
1819
  function isRealConstant(node) {
1461
1820
  // 真正的常量特征:
1462
1821
  // 1. 在模块顶层作用域(不在函数或块内)
1463
- // 2. 名称包含大写字母(表示意图作为常量)
1822
+ // 2. 名称符合常量命名模式(全大写或大写蛇形)
1823
+ // 3. 不是模块导入(避免将require导入识别为常量)
1464
1824
 
1465
1825
  // 检查是否在模块顶层
1466
1826
  let scope_node = node;
@@ -1476,9 +1836,23 @@ const variable_name_rule = {
1476
1836
  }
1477
1837
  }
1478
1838
 
1479
- // 检查名称是否包含大写字母(表示意图作为常量)
1839
+ // 检查是否是模块导入(避免将require导入识别为常量)
1840
+ if (node.init &&
1841
+ (node.init.type === "CallExpression" &&
1842
+ node.init.callee &&
1843
+ node.init.callee.name === "require")) {
1844
+ return false; // 这是模块导入,不是真正的常量
1845
+ }
1846
+
1847
+ // 检查名称是否符合常量命名模式
1480
1848
  const name = node.id.name;
1481
- return /[A-Z]/.test(name);
1849
+
1850
+ // 常量命名模式:全大写或大写蛇形
1851
+ const is_uppercase = /^[A-Z][A-Z0-9]*$/.test(name); // 全大写:PORT, DEBUG
1852
+ const is_upper_snake = /^[A-Z][A-Z0-9_]*(_[A-Z0-9]+)*$/.test(name); // 大写蛇形:API_BASE_URL
1853
+
1854
+ // 只有符合常量命名模式的才被认为是真正的常量
1855
+ return is_uppercase || is_upper_snake;
1482
1856
  }
1483
1857
 
1484
1858
  return {
@@ -1489,6 +1863,89 @@ const variable_name_rule = {
1489
1863
  return; // 这是真正的常量,由常量规则处理
1490
1864
  }
1491
1865
 
1866
+ // 检查是否是函数声明(由函数名规则处理,这里跳过)
1867
+ if (node.init &&
1868
+ (node.init.type === "FunctionExpression" ||
1869
+ node.init.type === "ArrowFunctionExpression")) {
1870
+ // 这是函数声明,由函数名规则处理,这里跳过避免重复检测
1871
+ return;
1872
+ }
1873
+
1874
+ // 检查是否是类实例
1875
+ if (node.init &&
1876
+ node.init.type === "NewExpression" &&
1877
+ node.init.callee &&
1878
+ node.init.callee.type === "Identifier" &&
1879
+ /^[A-Z][a-zA-Z]*$/.test(node.init.callee.name)) {
1880
+ // 这是类实例,应该由变量名规则处理(类实例是变量,不是类)
1881
+ const class_instance_name = node.id.name;
1882
+ const detector = new Detector({ "variable-name": options });
1883
+ const result = detector.checkName(class_instance_name, "variable-name");
1884
+
1885
+ if (!result.valid) {
1886
+ result.errors.forEach((error) => {
1887
+ context.report({
1888
+ node: node.id,
1889
+ message: `类实例名"${class_instance_name}"不符合规范: ${error}`,
1890
+ });
1891
+ });
1892
+ }
1893
+ return;
1894
+ }
1895
+
1896
+ // 检查是否是模块导入
1897
+ if (node.init &&
1898
+ node.init.type === "CallExpression" &&
1899
+ node.init.callee &&
1900
+ node.init.callee.name === "require") {
1901
+ // 这是模块导入,应用模块导入命名规则
1902
+ const module_name = node.id.name;
1903
+
1904
+ // 智能判断模块导入的命名风格
1905
+ if (/^[A-Z][a-zA-Z]*$/.test(module_name)) {
1906
+ // 类/构造函数导入:Koa, Express - 应该使用PascalCase
1907
+ const detector = new Detector({ "class-name": options });
1908
+ const result = detector.checkName(module_name, "class-name");
1909
+
1910
+ if (!result.valid) {
1911
+ result.errors.forEach((error) => {
1912
+ context.report({
1913
+ node: node.id,
1914
+ message: `模块导入名"${module_name}"不符合规范: ${error}`,
1915
+ });
1916
+ });
1917
+ }
1918
+ } else if (/^[a-z][a-zA-Z]*$/.test(module_name)) {
1919
+ // 默认导出导入:express, config - 应该使用camelCase
1920
+ const detector = new Detector({ "function-name": options });
1921
+ const result = detector.checkName(module_name, "function-name");
1922
+
1923
+ if (!result.valid) {
1924
+ result.errors.forEach((error) => {
1925
+ context.report({
1926
+ node: node.id,
1927
+ message: `模块导入名"${module_name}"不符合规范: ${error}`,
1928
+ });
1929
+ });
1930
+ }
1931
+ } else {
1932
+ // 其他情况(如path, fs) - 使用变量名规则
1933
+ const detector = new Detector({ "variable-name": options });
1934
+ const result = detector.checkName(module_name, "variable-name");
1935
+
1936
+ if (!result.valid) {
1937
+ result.errors.forEach((error) => {
1938
+ context.report({
1939
+ node: node.id,
1940
+ message: `模块导入名"${module_name}"不符合规范: ${error}`,
1941
+ });
1942
+ });
1943
+ }
1944
+ }
1945
+ return;
1946
+ }
1947
+
1948
+ // 真正的变量声明
1492
1949
  const variable_name = node.id.name;
1493
1950
  const detector = new Detector({ "variable-name": options });
1494
1951
  const result = detector.checkName(variable_name, "variable-name");
@@ -1516,7 +1973,7 @@ const constant_name_rule = {
1516
1973
  docs: {
1517
1974
  description: "常量名必须使用大写蛇形命名法(UPPER_SNAKE_CASE)",
1518
1975
  category: "Stylistic Issues",
1519
- recommended: true,
1976
+ recommen: true,
1520
1977
  },
1521
1978
  schema: [
1522
1979
  {
@@ -1540,8 +1997,8 @@ const constant_name_rule = {
1540
1997
  function isRealConstant(node) {
1541
1998
  // 真正的常量特征:
1542
1999
  // 1. 在模块顶层作用域(不在函数或块内)
1543
- // 2. 名称包含大写字母(表示意图作为常量)
1544
- // 3. 或者名称是常见的常量模式(如全大写)
2000
+ // 2. 名称符合常量命名模式(全大写或大写蛇形)
2001
+ // 3. 不是模块导入(避免将require导入识别为常量)
1545
2002
 
1546
2003
  // 检查是否在模块顶层
1547
2004
  let scope_node = node;
@@ -1557,9 +2014,23 @@ const constant_name_rule = {
1557
2014
  }
1558
2015
  }
1559
2016
 
1560
- // 检查名称是否包含大写字母(表示意图作为常量)
2017
+ // 检查是否是模块导入(避免将require导入识别为常量)
2018
+ if (node.init &&
2019
+ (node.init.type === "CallExpression" &&
2020
+ node.init.callee &&
2021
+ node.init.callee.name === "require")) {
2022
+ return false; // 这是模块导入,不是真正的常量
2023
+ }
2024
+
2025
+ // 检查名称是否符合常量命名模式
1561
2026
  const name = node.id.name;
1562
- return /[A-Z]/.test(name);
2027
+
2028
+ // 常量命名模式:全大写或大写蛇形
2029
+ const is_uppercase = /^[A-Z][A-Z0-9]*$/.test(name); // 全大写:PORT, DEBUG
2030
+ const is_upper_snake = /^[A-Z][A-Z0-9_]*(_[A-Z0-9]+)*$/.test(name); // 大写蛇形:API_BASE_URL
2031
+
2032
+ // 只有符合常量命名模式的才被认为是真正的常量
2033
+ return is_uppercase || is_upper_snake;
1563
2034
  }
1564
2035
 
1565
2036
  return {
@@ -1595,7 +2066,7 @@ const param_name_rule = {
1595
2066
  docs: {
1596
2067
  description: "入参名必须使用小写蛇形命名法(snake_case)",
1597
2068
  category: "Stylistic Issues",
1598
- recommended: true,
2069
+ recommen: true,
1599
2070
  },
1600
2071
  schema: [
1601
2072
  {
@@ -1664,7 +2135,7 @@ const prop_name_rule = {
1664
2135
  docs: {
1665
2136
  description: "属性名必须使用小写蛇形命名法(snake_case)",
1666
2137
  category: "Stylistic Issues",
1667
- recommended: true,
2138
+ recommen: true,
1668
2139
  },
1669
2140
  schema: [
1670
2141
  {
@@ -1726,7 +2197,7 @@ const private_method_rule = {
1726
2197
  docs: {
1727
2198
  description: "私有方法必须以单下划线开头,后跟小驼峰命名或小写单词",
1728
2199
  category: "Stylistic Issues",
1729
- recommended: true,
2200
+ recommen: true,
1730
2201
  },
1731
2202
  schema: [
1732
2203
  {
@@ -1779,7 +2250,7 @@ const private_variable_rule = {
1779
2250
  docs: {
1780
2251
  description: "私有变量必须以单下划线开头,后跟小写蛇形命名或小写单词",
1781
2252
  category: "Stylistic Issues",
1782
- recommended: true,
2253
+ recommen: true,
1783
2254
  },
1784
2255
  schema: [
1785
2256
  {
@@ -1832,7 +2303,7 @@ const instance_property_rule = {
1832
2303
  docs: {
1833
2304
  description: "实例属性赋值必须符合命名规范",
1834
2305
  category: "Stylistic Issues",
1835
- recommended: true,
2306
+ recommen: true,
1836
2307
  },
1837
2308
  schema: [
1838
2309
  {
@@ -1888,7 +2359,7 @@ const prototype_method_rule = {
1888
2359
  docs: {
1889
2360
  description: "原型方法名必须使用小驼峰命名法(camelCase)且优先使用单个单词",
1890
2361
  category: "Stylistic Issues",
1891
- recommended: true,
2362
+ recommen: true,
1892
2363
  },
1893
2364
  schema: [
1894
2365
  {
@@ -1917,11 +2388,11 @@ const prototype_method_rule = {
1917
2388
  node.left.object.property.type === "Identifier" &&
1918
2389
  node.left.object.property.name === "prototype" &&
1919
2390
  node.left.property.type === "Identifier" &&
1920
- (node.right.type === "FunctionExpression" ||
1921
- node.right.type === "ArrowFunctionExpression")
2391
+ (node.right.type === "FunctionExpression" ||
2392
+ node.right.type === "ArrowFunctionExpression")
1922
2393
  ) {
1923
2394
  const method_name = node.left.property.name;
1924
-
2395
+
1925
2396
  // 跳过JavaScript内置特殊方法名
1926
2397
  const builtin_methods = [
1927
2398
  "constructor",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_eslint",
3
- "version": "1.0.8",
3
+ "version": "1.1.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": [
@@ -32,7 +32,7 @@
32
32
  "eslint": ">=8.0.0"
33
33
  },
34
34
  "scripts": {
35
- "test": "node test.js",
35
+ "test": "node tests/test_new_recommen_words.js",
36
36
  "lint": "npx eslint . --config eslint.config.js"
37
37
  },
38
38
  "files": [