@lsby/eslint-plugin 0.0.16 → 0.0.18
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/README.md +0 -37
- package/dist/index.d.ts +1 -0
- package/dist/index.js +21 -0
- package/dist/lib/rules/no-broken-link.d.ts +3 -0
- package/dist/lib/rules/no-broken-link.js +33 -0
- package/dist/lib/rules/no-definite-assignment-assertion.d.ts +3 -0
- package/dist/lib/rules/no-definite-assignment-assertion.js +22 -0
- package/dist/lib/rules/no-else-on-equality.d.ts +3 -0
- package/dist/lib/rules/no-else-on-equality.js +40 -0
- package/dist/lib/rules/no-negation.d.ts +3 -0
- package/dist/lib/rules/no-negation.js +34 -0
- package/dist/lib/rules/no-switch-default.d.ts +3 -0
- package/dist/lib/rules/no-switch-default.js +27 -0
- package/dist/lib/rules/prefer-let.d.ts +3 -0
- package/dist/lib/rules/prefer-let.js +56 -0
- package/dist/lib/rules/prefer-switch-over-multi-if.d.ts +3 -0
- package/dist/lib/rules/prefer-switch-over-multi-if.js +108 -0
- package/package.json +22 -6
- package/.prettierignore +0 -51
- package/.prettierrc +0 -9
- package/index.js +0 -9
- package/lib/rules/no-broken-link.js +0 -41
- package/lib/rules/no-definite-assignment-assertion.js +0 -35
- package/lib/rules/no-else.js +0 -30
- package/lib/rules/no-negation.js +0 -17
- package/lib/rules/prefer-let.js +0 -43
package/README.md
CHANGED
|
@@ -1,40 +1,3 @@
|
|
|
1
1
|
# eslint-plugin
|
|
2
2
|
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
3
|
一些自己写的eslint插件
|
|
6
|
-
|
|
7
|
-
## 安装
|
|
8
|
-
|
|
9
|
-
1. **安装包**
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
npm i @lsby/eslint-plugin
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
2. **安装 ESLint**
|
|
16
|
-
|
|
17
|
-
确保你的项目中已安装 ESLint 和 TypeScript ESLint 解析器。如果尚未安装,可以使用以下命令进行安装:
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
3. **配置 ESLint**
|
|
24
|
-
|
|
25
|
-
在你的 ESLint 配置文件中引入并启用规则:
|
|
26
|
-
|
|
27
|
-
```javascript
|
|
28
|
-
module.exports = {
|
|
29
|
-
parser: '@typescript-eslint/parser',
|
|
30
|
-
plugins: ['@typescript-eslint'],
|
|
31
|
-
extends: [
|
|
32
|
-
'eslint:recommended',
|
|
33
|
-
'plugin:@typescript-eslint/recommended'
|
|
34
|
-
],
|
|
35
|
-
rules: {
|
|
36
|
-
'@lsby/no-broken-link': 'error',
|
|
37
|
-
'@lsby/prefer-let': 'error',
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const no_definite_assignment_assertion_1 = __importDefault(require("./lib/rules/no-definite-assignment-assertion"));
|
|
7
|
+
const no_else_on_equality_1 = __importDefault(require("./lib/rules/no-else-on-equality"));
|
|
8
|
+
const no_negation_1 = __importDefault(require("./lib/rules/no-negation"));
|
|
9
|
+
const no_switch_default_1 = __importDefault(require("./lib/rules/no-switch-default"));
|
|
10
|
+
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
|
+
module.exports = {
|
|
13
|
+
rules: {
|
|
14
|
+
'prefer-let': prefer_let_1.default,
|
|
15
|
+
'no-negation': no_negation_1.default,
|
|
16
|
+
'no-definite-assignment-assertion': no_definite_assignment_assertion_1.default,
|
|
17
|
+
'no-else-on-equality': no_else_on_equality_1.default,
|
|
18
|
+
'no-switch-default': no_switch_default_1.default,
|
|
19
|
+
'prefer-switch-over-multi-if': prefer_switch_over_multi_if_1.default,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// jsdoc的link必须存在
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const rule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: 'problem',
|
|
7
|
+
docs: { description: '禁止在 JSDoc 注解中使用未定义的 {@link} 引用' },
|
|
8
|
+
messages: { undefinedLink: '{@link} 中的标识符 "{identifier}" 未定义' },
|
|
9
|
+
schema: [],
|
|
10
|
+
},
|
|
11
|
+
create(context) {
|
|
12
|
+
return {
|
|
13
|
+
Program(node) {
|
|
14
|
+
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
15
|
+
const comments = sourceCode.getAllComments?.() || [];
|
|
16
|
+
comments.forEach((comment) => {
|
|
17
|
+
const matches = comment.value.match(/\{@link\s+([^\s}]+)\s*\}/g);
|
|
18
|
+
if (matches) {
|
|
19
|
+
matches.forEach((link) => {
|
|
20
|
+
const identifier = link.match(/\{@link\s+([^\s}]+)\s*\}/)[1];
|
|
21
|
+
const variables = context.getScope().variables;
|
|
22
|
+
const isDefined = variables.some((variable) => variable.name === identifier);
|
|
23
|
+
if (!isDefined) {
|
|
24
|
+
context.report({ node, messageId: 'undefinedLink', data: { identifier } });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
exports.default = rule;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 禁止类属性使用确定赋值断言语法, 否则很容易出现空指针
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const rule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: 'problem',
|
|
7
|
+
docs: { description: '禁止在类属性上使用明确赋值断言 (!:)' },
|
|
8
|
+
messages: { noDefiniteAssignment: '类属性不允许使用明确赋值断言 (!:), 应使用初始化或构造函数赋值' },
|
|
9
|
+
schema: [],
|
|
10
|
+
},
|
|
11
|
+
defaultOptions: [],
|
|
12
|
+
create(context) {
|
|
13
|
+
return {
|
|
14
|
+
PropertyDefinition(node) {
|
|
15
|
+
if (node.definite === true) {
|
|
16
|
+
context.report({ node, messageId: 'noDefiniteAssignment' });
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
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;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 禁止对非布尔值使用 "!" 运算符, 否则可能导致意外的类型强制转换
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
const rule = {
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem',
|
|
11
|
+
docs: { description: '禁止对非布尔值使用逻辑取反操作符 (!)' },
|
|
12
|
+
messages: { noNegationOnNonBoolean: '禁止对非布尔值使用 "!" 运算符' },
|
|
13
|
+
schema: [],
|
|
14
|
+
},
|
|
15
|
+
create(context) {
|
|
16
|
+
return {
|
|
17
|
+
UnaryExpression(node) {
|
|
18
|
+
if (node.operator !== '!')
|
|
19
|
+
return;
|
|
20
|
+
const parserServices = context.parserServices;
|
|
21
|
+
if (!parserServices)
|
|
22
|
+
return;
|
|
23
|
+
const typeChecker = parserServices.program.getTypeChecker();
|
|
24
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.argument);
|
|
25
|
+
const argumentType = typeChecker.getTypeAtLocation(tsNode);
|
|
26
|
+
const isBoolean = (argumentType.flags & typescript_1.default.TypeFlags.Boolean) !== 0 || (argumentType.flags & typescript_1.default.TypeFlags.BooleanLiteral) !== 0;
|
|
27
|
+
if (!isBoolean) {
|
|
28
|
+
context.report({ node, messageId: 'noNegationOnNonBoolean' });
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
exports.default = rule;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 禁止 switch 中使用 default 分支
|
|
3
|
+
// default 分支会掩盖新增的 case, 导致代码不可演化
|
|
4
|
+
// 如果未来增加新的 case, default 会让错误悄悄通过编译
|
|
5
|
+
// 通过禁止 default, 可以强制显式列出所有可能的分支
|
|
6
|
+
// 这样 switch 永远保持穷举, 提升类型安全和可维护性
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const rule = {
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem',
|
|
11
|
+
docs: { description: '禁止使用 switch 的 default 分支' },
|
|
12
|
+
messages: { noSwitchDefault: '禁止在 switch 语句中使用 default 分支' },
|
|
13
|
+
schema: [],
|
|
14
|
+
},
|
|
15
|
+
create(context) {
|
|
16
|
+
return {
|
|
17
|
+
SwitchStatement(node) {
|
|
18
|
+
for (const switchCase of node.cases) {
|
|
19
|
+
if (switchCase.test === null) {
|
|
20
|
+
context.report({ node: switchCase, messageId: 'noSwitchDefault' });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
exports.default = rule;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 永远使用let, 拒绝var和const, 并自动修复
|
|
3
|
+
// 原因:
|
|
4
|
+
// 1 抽象泄漏
|
|
5
|
+
// const只约束原语值和指针不变, 不约束引用值本身不变
|
|
6
|
+
// 使用者要搞懂什么可变什么不可变, 必须理解原语值和引用值的区别, 以及变量在内存上的机制
|
|
7
|
+
// 这种"必须搞懂背后的原理才能正常使用"的情况是显然的抽象泄漏
|
|
8
|
+
// 2 误导
|
|
9
|
+
// 对于新人, 很容易被误解, 导致引用值被意外修改
|
|
10
|
+
// 3 心智成本
|
|
11
|
+
// 实际使用时, 即使是const声明值, 也必须确认其是否为引用类型, 若是则还是需要确认所有可能的修改
|
|
12
|
+
// 这与let无异, 反而增加了心智成本
|
|
13
|
+
// **错误的信息比没有信息更糟糕**
|
|
14
|
+
// 替代:
|
|
15
|
+
// 如果真的需要表达不变, 应该使用类型等级的递归只读, 建模隐藏等方法
|
|
16
|
+
// 这虽然复杂度更高, 但可以真正保证安全
|
|
17
|
+
// 这也迫使程序员思考是否真的有必要这样设计, 而不是"随手一用", 提供一个"虚假的安全感"
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
const rule = {
|
|
20
|
+
meta: {
|
|
21
|
+
type: 'suggestion',
|
|
22
|
+
docs: { description: '禁止使用 const 和 var,仅允许使用 let 声明变量' },
|
|
23
|
+
fixable: 'code',
|
|
24
|
+
messages: { preferLet: '使用 let 代替 {kind}' },
|
|
25
|
+
schema: [],
|
|
26
|
+
},
|
|
27
|
+
defaultOptions: [],
|
|
28
|
+
create(context) {
|
|
29
|
+
return {
|
|
30
|
+
VariableDeclaration(node) {
|
|
31
|
+
// 跳过 unique symbol
|
|
32
|
+
if (node.kind === 'const' || node.kind === 'var') {
|
|
33
|
+
if (!('declare' in node && node.declare === true)) {
|
|
34
|
+
const hasUniqueSymbol = node.declarations.some((decl) => {
|
|
35
|
+
const typeAnn = decl.id.typeAnnotation?.typeAnnotation;
|
|
36
|
+
return typeAnn?.type === 'TSTypeOperator' && typeAnn.operator === 'unique';
|
|
37
|
+
});
|
|
38
|
+
if (hasUniqueSymbol)
|
|
39
|
+
return;
|
|
40
|
+
context.report({
|
|
41
|
+
node,
|
|
42
|
+
messageId: 'preferLet',
|
|
43
|
+
data: { kind: node.kind },
|
|
44
|
+
fix(fixer) {
|
|
45
|
+
if (!node.range)
|
|
46
|
+
return null;
|
|
47
|
+
return fixer.replaceTextRange([node.range[0], node.range[0] + node.kind.length], 'let');
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
exports.default = rule;
|
|
@@ -0,0 +1,108 @@
|
|
|
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;
|
package/package.json
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lsby/eslint-plugin",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"main": "index.js",
|
|
3
|
+
"version": "0.0.18",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
5
9
|
"scripts": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
10
|
+
"build": "rimraf dist && tsc",
|
|
11
|
+
"check:all": "npm run check:format && npm run check:type",
|
|
12
|
+
"check:format": "prettier --write .",
|
|
13
|
+
"check:type": "tsc --noEmit",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"pub:public": "npm run build && npm run test && bumpp && npm publish --access public",
|
|
16
|
+
"test": "npm run build && mocha test/**/*.test.js"
|
|
8
17
|
},
|
|
9
18
|
"devDependencies": {
|
|
10
19
|
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
|
|
20
|
+
"@types/eslint": "^8.56.0",
|
|
21
|
+
"@types/estree": "^1.0.8",
|
|
22
|
+
"@types/node": "^20.0.0",
|
|
23
|
+
"@typescript-eslint/parser": "^8.50.1",
|
|
24
|
+
"@typescript-eslint/utils": "^8.50.1",
|
|
11
25
|
"bumpp": "^9.5.1",
|
|
12
26
|
"chai": "^5.1.1",
|
|
13
27
|
"mocha": "^10.7.3",
|
|
14
|
-
"prettier": "^3.
|
|
15
|
-
"prettier-plugin-packagejson": "^2.5.
|
|
28
|
+
"prettier": "^3.7.4",
|
|
29
|
+
"prettier-plugin-packagejson": "^2.5.20",
|
|
30
|
+
"rimraf": "^6.1.2",
|
|
31
|
+
"typescript": "^5.3.0"
|
|
16
32
|
},
|
|
17
33
|
"peerDependencies": {
|
|
18
34
|
"eslint": "^8.56.0"
|
package/.prettierignore
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# 忽略 node_modules 目录
|
|
2
|
-
node_modules/
|
|
3
|
-
|
|
4
|
-
# 忽略所有日志文件
|
|
5
|
-
*.log
|
|
6
|
-
|
|
7
|
-
# 忽略构建输出目录
|
|
8
|
-
dist/
|
|
9
|
-
build/
|
|
10
|
-
|
|
11
|
-
# 忽略环境变量文件
|
|
12
|
-
.env
|
|
13
|
-
.env.local
|
|
14
|
-
.env.*.local
|
|
15
|
-
|
|
16
|
-
# 忽略锁文件
|
|
17
|
-
pnpm-lock.yaml
|
|
18
|
-
yarn.lock
|
|
19
|
-
package-lock.json
|
|
20
|
-
|
|
21
|
-
# 忽略 IDE 配置文件
|
|
22
|
-
.vscode/
|
|
23
|
-
.idea/
|
|
24
|
-
|
|
25
|
-
# 忽略临时文件
|
|
26
|
-
*.tmp
|
|
27
|
-
*.temp
|
|
28
|
-
|
|
29
|
-
# 忽略特定文件类型
|
|
30
|
-
*.min.js
|
|
31
|
-
*.min.css
|
|
32
|
-
|
|
33
|
-
# 忽略特定文件
|
|
34
|
-
pnpm-lock.yaml
|
|
35
|
-
|
|
36
|
-
# 忽略图片和其他媒体文件
|
|
37
|
-
*.png
|
|
38
|
-
*.jpg
|
|
39
|
-
*.jpeg
|
|
40
|
-
*.gif
|
|
41
|
-
*.svg
|
|
42
|
-
|
|
43
|
-
# 忽略压缩文件
|
|
44
|
-
*.zip
|
|
45
|
-
*.tar.gz
|
|
46
|
-
*.rar
|
|
47
|
-
|
|
48
|
-
# 忽略文档
|
|
49
|
-
*.pdf
|
|
50
|
-
*.docx
|
|
51
|
-
*.xlsx
|
package/.prettierrc
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"printWidth": 120,
|
|
3
|
-
"trailingComma": "all",
|
|
4
|
-
"semi": false,
|
|
5
|
-
"endOfLine": "auto",
|
|
6
|
-
"singleQuote": true,
|
|
7
|
-
"plugins": ["prettier-plugin-packagejson", "@ianvs/prettier-plugin-sort-imports"],
|
|
8
|
-
"importOrder": ["<BUILTIN_MODULES>", "<THIRD_PARTY_MODULES>", "^@([^/]+?)/(.*)$", "^[./]"]
|
|
9
|
-
}
|
package/index.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
rules: {
|
|
3
|
-
'no-broken-link': require('./lib/rules/no-broken-link'),
|
|
4
|
-
'prefer-let': require('./lib/rules/prefer-let'),
|
|
5
|
-
'no-negation': require('./lib/rules/no-negation'),
|
|
6
|
-
'no-definite-assignment-assertion': require('./lib/rules/no-definite-assignment-assertion'),
|
|
7
|
-
'no-else': require('./lib/rules/no-else'),
|
|
8
|
-
},
|
|
9
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// jsdoc的link必须存在
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
meta: {
|
|
5
|
-
type: 'problem', // 规则类型,可以是 "problem", "suggestion", 或 "layout"
|
|
6
|
-
docs: {
|
|
7
|
-
description: 'Check for broken {@link} references in comments',
|
|
8
|
-
category: 'Possible Errors',
|
|
9
|
-
recommended: false,
|
|
10
|
-
},
|
|
11
|
-
schema: [], // 规则的选项配置
|
|
12
|
-
},
|
|
13
|
-
create: function (context) {
|
|
14
|
-
return {
|
|
15
|
-
Program(node) {
|
|
16
|
-
const sourceCode = context.getSourceCode()
|
|
17
|
-
const comments = sourceCode.getAllComments()
|
|
18
|
-
|
|
19
|
-
comments.forEach((comment) => {
|
|
20
|
-
const matches = comment.value.match(/\{@link\s+([^\s}]+)\s*\}/g)
|
|
21
|
-
|
|
22
|
-
if (matches) {
|
|
23
|
-
matches.forEach((link) => {
|
|
24
|
-
const identifier = link.match(/\{@link\s+([^\s}]+)\s*\}/)[1]
|
|
25
|
-
|
|
26
|
-
const variables = context.getScope().variables
|
|
27
|
-
const isDefined = variables.some((variable) => variable.name === identifier)
|
|
28
|
-
|
|
29
|
-
if (!isDefined) {
|
|
30
|
-
context.report({
|
|
31
|
-
node: comment,
|
|
32
|
-
message: `{@link}中的标识符“${identifier}”没有定义。`,
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
},
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// 禁止类属性使用确定赋值断言语法
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
meta: {
|
|
5
|
-
type: 'problem',
|
|
6
|
-
docs: {
|
|
7
|
-
description: '禁止类属性使用确定赋值断言 (foo!: T)',
|
|
8
|
-
recommended: 'error',
|
|
9
|
-
},
|
|
10
|
-
messages: {
|
|
11
|
-
noDefiniteAssignment: '类属性不允许使用确定赋值断言 (!:)',
|
|
12
|
-
},
|
|
13
|
-
schema: [],
|
|
14
|
-
},
|
|
15
|
-
create(context) {
|
|
16
|
-
return {
|
|
17
|
-
ClassProperty(node) {
|
|
18
|
-
if (node.definite) {
|
|
19
|
-
context.report({
|
|
20
|
-
node,
|
|
21
|
-
messageId: 'noDefiniteAssignment',
|
|
22
|
-
})
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
PropertyDefinition(node) {
|
|
26
|
-
if (node.definite) {
|
|
27
|
-
context.report({
|
|
28
|
-
node,
|
|
29
|
-
messageId: 'noDefiniteAssignment',
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
}
|
package/lib/rules/no-else.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// 禁止 else
|
|
2
|
-
// else 表示"除当前条件外的所有可能"
|
|
3
|
-
// 当状态集合未来扩展时, 依然会被包含在else分支里, 导致状态遗漏却无任何报错
|
|
4
|
-
// 应当使用提早返回 或 switch + 穷尽检查
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: 'problem',
|
|
9
|
-
docs: {
|
|
10
|
-
description: '禁止使用 else, 避免不稳定的否定剩余分支',
|
|
11
|
-
},
|
|
12
|
-
messages: {
|
|
13
|
-
noElse: '禁止使用 else, 请改用 early return 或 switch',
|
|
14
|
-
},
|
|
15
|
-
schema: [],
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
create(context) {
|
|
19
|
-
return {
|
|
20
|
-
IfStatement(node) {
|
|
21
|
-
if (node.alternate !== null) {
|
|
22
|
-
context.report({
|
|
23
|
-
node: node.alternate,
|
|
24
|
-
messageId: 'noElse',
|
|
25
|
-
})
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
}
|
package/lib/rules/no-negation.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// 禁止对非布尔值使用取反
|
|
2
|
-
// 对于 number | null 的值x, if(!x)在x等于null和0时都会触发, 这可能是非预期的
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
create(context) {
|
|
6
|
-
return {
|
|
7
|
-
UnaryExpression(node) {
|
|
8
|
-
if (node.operator === '!' && !(node.argument.type === 'Literal' && typeof node.argument.value === 'boolean')) {
|
|
9
|
-
context.report({
|
|
10
|
-
node,
|
|
11
|
-
message: "禁止对非布尔值使用 '!' 运算符。",
|
|
12
|
-
})
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
}
|
package/lib/rules/prefer-let.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// 永远使用let, 拒绝var和const, 并自动修复
|
|
2
|
-
// 禁止const是因为它有抽象泄漏
|
|
3
|
-
// 它只对原语值和指针不变, 不对引用值本身不变
|
|
4
|
-
// 要搞懂什么可变什么不可变, 必须理解原语值和引用值的区别, 以及变量在内存上的机制
|
|
5
|
-
// 如果真的需要表达不变, 应该在类型等级写递归只读
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
meta: {
|
|
9
|
-
type: 'suggestion',
|
|
10
|
-
docs: {
|
|
11
|
-
description: 'Replace const and var with let',
|
|
12
|
-
category: 'Best Practices',
|
|
13
|
-
recommended: false,
|
|
14
|
-
},
|
|
15
|
-
fixable: 'code',
|
|
16
|
-
schema: [],
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
create(context) {
|
|
20
|
-
return {
|
|
21
|
-
VariableDeclaration(node) {
|
|
22
|
-
// 跳过 unique symbol
|
|
23
|
-
if ((node.kind === 'const' || node.kind === 'var') && !node.declare) {
|
|
24
|
-
let hasUniqueSymbol = node.declarations.some((decl) => {
|
|
25
|
-
const typeAnn = decl.id.typeAnnotation?.typeAnnotation
|
|
26
|
-
return typeAnn?.type === 'TSTypeOperator' && typeAnn.operator === 'unique'
|
|
27
|
-
})
|
|
28
|
-
if (hasUniqueSymbol) return
|
|
29
|
-
|
|
30
|
-
// 使用修复功能将 const 或 var 替换为 let
|
|
31
|
-
context.report({
|
|
32
|
-
node,
|
|
33
|
-
message: '使用let代替const或var',
|
|
34
|
-
fix(fixer) {
|
|
35
|
-
const range = node.range
|
|
36
|
-
return fixer.replaceTextRange([range[0], range[0] + node.kind.length], 'let')
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
}
|