@lsby/eslint-plugin 0.0.17 → 0.0.19

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/dist/index.js CHANGED
@@ -4,18 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const no_definite_assignment_assertion_1 = __importDefault(require("./lib/rules/no-definite-assignment-assertion"));
7
- const no_else_1 = __importDefault(require("./lib/rules/no-else"));
7
+ const no_early_return_on_equality_1 = __importDefault(require("./lib/rules/no-early-return-on-equality"));
8
+ const no_else_on_equality_1 = __importDefault(require("./lib/rules/no-else-on-equality"));
8
9
  const no_negation_1 = __importDefault(require("./lib/rules/no-negation"));
9
10
  const no_switch_default_1 = __importDefault(require("./lib/rules/no-switch-default"));
10
11
  const prefer_let_1 = __importDefault(require("./lib/rules/prefer-let"));
11
- const prefer_switch_over_multi_if_1 = __importDefault(require("./lib/rules/prefer-switch-over-multi-if"));
12
12
  module.exports = {
13
13
  rules: {
14
14
  'prefer-let': prefer_let_1.default,
15
15
  'no-negation': no_negation_1.default,
16
16
  'no-definite-assignment-assertion': no_definite_assignment_assertion_1.default,
17
- 'no-else': no_else_1.default,
17
+ 'no-early-return-on-equality': no_early_return_on_equality_1.default,
18
+ 'no-else-on-equality': no_else_on_equality_1.default,
18
19
  'no-switch-default': no_switch_default_1.default,
19
- 'prefer-switch-over-multi-if': prefer_switch_over_multi_if_1.default,
20
20
  },
21
21
  };
@@ -1,3 +1,3 @@
1
1
  import type { TSESLint } from '@typescript-eslint/utils';
2
- declare const rule: TSESLint.RuleModule<'preferSwitch', []>;
2
+ declare const rule: TSESLint.RuleModule<'noEarlyReturnOnEquality', []>;
3
3
  export default rule;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ // 禁止等于条件的if提前返回,但允许不等于条件的提前返回
3
+ // 使用等于条件的提前返回容易遗漏状态,应该用正向的不等于条件或结构化流程
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ const rule = {
6
+ meta: {
7
+ type: 'problem',
8
+ docs: {
9
+ description: '禁止在等于条件下使用提前返回(early return)。使用不等于条件的提前返回会更清晰地表达程序的主要流程',
10
+ },
11
+ messages: {
12
+ noEarlyReturnOnEquality: '禁止在等于条件下使用提前返回。建议改用不等于条件的提前返回,使主流程更清晰。例如:将 if (x === y) return; 改为 if (x !== y) { 主要逻辑 }',
13
+ },
14
+ schema: [],
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ /**
19
+ * 检查条件中是否是等于操作符(不包括不等于)
20
+ */
21
+ function isEqualityOperator(node) {
22
+ if (node.type === 'BinaryExpression') {
23
+ return ['===', '=='].includes(node.operator);
24
+ }
25
+ if (node.type === 'LogicalExpression') {
26
+ return isEqualityOperator(node.left) || isEqualityOperator(node.right);
27
+ }
28
+ return false;
29
+ }
30
+ /**
31
+ * 检查语句是否是提前返回或抛出(仅包含这些语句)
32
+ */
33
+ function isEarlyExit(node) {
34
+ if (!node)
35
+ return false;
36
+ // 直接是 return 或 throw
37
+ if (node.type === 'ReturnStatement' || node.type === 'ThrowStatement') {
38
+ return true;
39
+ }
40
+ // 是 block statement,检查其中的所有语句是否都是 return 或 throw
41
+ if (node.type === 'BlockStatement') {
42
+ return (node.body.length > 0 &&
43
+ node.body.every((stmt) => stmt.type === 'ReturnStatement' || stmt.type === 'ThrowStatement'));
44
+ }
45
+ return false;
46
+ }
47
+ return {
48
+ IfStatement(node) {
49
+ // 检查:条件是等于 & if 分支是提前返回 & 没有 else 分支
50
+ if (!node.alternate && isEqualityOperator(node.test) && isEarlyExit(node.consequent)) {
51
+ context.report({ node, messageId: 'noEarlyReturnOnEquality' });
52
+ }
53
+ },
54
+ };
55
+ },
56
+ };
57
+ exports.default = rule;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ // 禁止 else(当条件是等于或不等于时)
3
+ // 对于等于/不等于的条件判断,else 表示"除当前条件外的所有可能"
4
+ // 当状态集合未来扩展时, 依然会被包含在else分支里, 导致状态遗漏却无任何报错
5
+ // 应当使用提早返回 或 switch + 穷尽检查
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const rule = {
8
+ meta: {
9
+ type: 'problem',
10
+ docs: { description: '当条件是等于或不等于时,禁止使用 else 分支,防止隐含的状态遗漏' },
11
+ messages: {
12
+ noElse: '禁止在等于/不等于条件下使用 else 分支。当条件状态扩展时, else 分支可能遗漏新的状态。请改用提前返回(early return)或 switch 语句',
13
+ },
14
+ schema: [],
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ /**
19
+ * 检查条件中是否包含等于或不等于操作符
20
+ */
21
+ function hasEqualityOperator(node) {
22
+ if (node.type === 'BinaryExpression') {
23
+ return ['===', '!==', '==', '!='].includes(node.operator);
24
+ }
25
+ if (node.type === 'LogicalExpression') {
26
+ return hasEqualityOperator(node.left) || hasEqualityOperator(node.right);
27
+ }
28
+ return false;
29
+ }
30
+ return {
31
+ IfStatement(node) {
32
+ // 仅在 if 条件是等于或不等于的情况下,禁止 else
33
+ if (node.alternate !== null && hasEqualityOperator(node.test)) {
34
+ context.report({ node: node.alternate, messageId: 'noElse' });
35
+ }
36
+ },
37
+ };
38
+ },
39
+ };
40
+ exports.default = rule;
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@lsby/eslint-plugin",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
7
7
  "dist"
8
8
  ],
9
9
  "scripts": {
10
- "build": "tsc",
10
+ "build": "rimraf dist && tsc",
11
11
  "check:all": "npm run check:format && npm run check:type",
12
12
  "check:format": "prettier --write .",
13
13
  "check:type": "tsc --noEmit",
14
14
  "dev": "tsc --watch",
15
15
  "pub:public": "npm run build && npm run test && bumpp && npm publish --access public",
16
- "test": "mocha test/**/*.test.js"
16
+ "test": "npm run build && mocha test/**/*.test.js"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@ianvs/prettier-plugin-sort-imports": "^4.2.1",
@@ -27,6 +27,7 @@
27
27
  "mocha": "^10.7.3",
28
28
  "prettier": "^3.7.4",
29
29
  "prettier-plugin-packagejson": "^2.5.20",
30
+ "rimraf": "^6.1.2",
30
31
  "typescript": "^5.3.0"
31
32
  },
32
33
  "peerDependencies": {
@@ -1,27 +0,0 @@
1
- "use strict";
2
- // 禁止 else
3
- // else 表示"除当前条件外的所有可能"
4
- // 当状态集合未来扩展时, 依然会被包含在else分支里, 导致状态遗漏却无任何报错
5
- // 应当使用提早返回 或 switch + 穷尽检查
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const rule = {
8
- meta: {
9
- type: 'problem',
10
- docs: { description: '禁止使用 else 分支, 防止隐含的状态遗漏' },
11
- messages: {
12
- noElse: '禁止使用 else 分支。当条件状态扩展时, else 分支可能遗漏新的状态。请改用提前返回(early return)或 switch 语句',
13
- },
14
- schema: [],
15
- },
16
- defaultOptions: [],
17
- create(context) {
18
- return {
19
- IfStatement(node) {
20
- if (node.alternate !== null) {
21
- context.report({ node: node.alternate, messageId: 'noElse' });
22
- }
23
- },
24
- };
25
- },
26
- };
27
- exports.default = rule;
@@ -1,108 +0,0 @@
1
- "use strict";
2
- // 建议使用 switch 替代多个 if 判断同一变量
3
- // 当有多个连续的 if 语句判断同一个表达式时, 应该使用 switch 来提高代码的可读性
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- const rule = {
6
- meta: {
7
- type: 'suggestion',
8
- docs: { description: '检测多个连续 if 判断同一个表达式,建议使用 switch 提高可读性' },
9
- messages: { preferSwitch: '检测到 {{count}} 个连续的 if 判断同一个表达式,可以考虑使用 switch 提高可读性' },
10
- schema: [],
11
- },
12
- defaultOptions: [],
13
- create(context) {
14
- const sourceCode = context.sourceCode;
15
- const minIfs = 2;
16
- function getText(node) {
17
- return sourceCode.getText(node);
18
- }
19
- function isValidCaseValue(node) {
20
- return (node.type === 'Literal' ||
21
- node.type === 'TemplateLiteral' ||
22
- node.type === 'Identifier' ||
23
- node.type === 'MemberExpression');
24
- }
25
- function getSwitchInfo(test) {
26
- if (test.type !== 'BinaryExpression')
27
- return null;
28
- // 支持所有的二元比较操作符
29
- const comparisonOps = ['===', '==', '!==', '!=', '>', '<', '>=', '<='];
30
- if (!comparisonOps.includes(test.operator))
31
- return null;
32
- const left = test.left;
33
- const right = test.right;
34
- if ((left.type === 'Identifier' || left.type === 'MemberExpression') && isValidCaseValue(right)) {
35
- return { variable: getText(left), value: getText(right) };
36
- }
37
- if ((right.type === 'Identifier' || right.type === 'MemberExpression') && isValidCaseValue(left)) {
38
- return { variable: getText(right), value: getText(left) };
39
- }
40
- return null;
41
- }
42
- function collectIfChain(node) {
43
- const chain = [node];
44
- let current = node;
45
- // 收集 else-if 链
46
- while (current.alternate && current.alternate.type === 'IfStatement') {
47
- chain.push(current.alternate);
48
- current = current.alternate;
49
- }
50
- // 如果没有 else-if,尝试收集相邻的 if 语句
51
- if (chain.length === 1 && node.parent) {
52
- const parent = node.parent;
53
- let body = null;
54
- if (parent.type === 'BlockStatement') {
55
- body = parent.body;
56
- }
57
- else if (parent.type === 'Program') {
58
- body = parent.body;
59
- }
60
- if (body) {
61
- const nodeIndex = body.indexOf(node);
62
- if (nodeIndex !== -1) {
63
- const firstInfo = getSwitchInfo(node.test);
64
- if (firstInfo) {
65
- let nextIndex = nodeIndex + 1;
66
- while (nextIndex < body.length && body[nextIndex].type === 'IfStatement') {
67
- const nextIfStmt = body[nextIndex];
68
- if (nextIfStmt.alternate)
69
- break; // 如果有 else/else-if,停止
70
- const nextInfo = getSwitchInfo(nextIfStmt.test);
71
- if (!nextInfo || nextInfo.variable !== firstInfo.variable)
72
- break; // 变量不同,停止
73
- chain.push(nextIfStmt);
74
- nextIndex++;
75
- }
76
- }
77
- }
78
- }
79
- }
80
- return chain;
81
- }
82
- function allSameVariable(chain) {
83
- if (chain.length < minIfs)
84
- return null;
85
- const firstInfo = getSwitchInfo(chain[0].test);
86
- if (!firstInfo)
87
- return null;
88
- for (const stmt of chain.slice(1)) {
89
- const info = getSwitchInfo(stmt.test);
90
- if (!info || info.variable !== firstInfo.variable)
91
- return null;
92
- }
93
- return { variable: firstInfo.variable, count: chain.length };
94
- }
95
- return {
96
- IfStatement(node) {
97
- if (node.parent?.type === 'IfStatement' && node.parent.alternate === node)
98
- return;
99
- const chain = collectIfChain(node);
100
- const info = allSameVariable(chain);
101
- if (!info)
102
- return;
103
- context.report({ node, messageId: 'preferSwitch', data: { count: info.count } });
104
- },
105
- };
106
- },
107
- };
108
- exports.default = rule;