eslint-plugin-complete 1.0.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/LICENSE +9 -0
- package/README.md +114 -0
- package/dist/comments.d.ts +22 -0
- package/dist/comments.d.ts.map +1 -0
- package/dist/comments.js +66 -0
- package/dist/completeCommon.d.ts +25 -0
- package/dist/completeCommon.d.ts.map +1 -0
- package/dist/completeCommon.js +53 -0
- package/dist/completeSentence.d.ts +9 -0
- package/dist/completeSentence.d.ts.map +1 -0
- package/dist/completeSentence.js +267 -0
- package/dist/configs/recommended.d.ts +3 -0
- package/dist/configs/recommended.d.ts.map +1 -0
- package/dist/configs/recommended.js +63 -0
- package/dist/configs.d.ts +4 -0
- package/dist/configs.d.ts.map +1 -0
- package/dist/configs.js +4 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +73 -0
- package/dist/format.d.ts +18 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +246 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/interfaces/MyPluginDocs.d.ts +6 -0
- package/dist/interfaces/MyPluginDocs.d.ts.map +1 -0
- package/dist/interfaces/MyPluginDocs.js +1 -0
- package/dist/jsdoc.d.ts +4 -0
- package/dist/jsdoc.d.ts.map +1 -0
- package/dist/jsdoc.js +24 -0
- package/dist/leadingLineComments.d.ts +32 -0
- package/dist/leadingLineComments.d.ts.map +1 -0
- package/dist/leadingLineComments.js +77 -0
- package/dist/list.d.ts +49 -0
- package/dist/list.d.ts.map +1 -0
- package/dist/list.js +140 -0
- package/dist/rules/complete-sentences-jsdoc.d.ts +4 -0
- package/dist/rules/complete-sentences-jsdoc.d.ts.map +1 -0
- package/dist/rules/complete-sentences-jsdoc.js +48 -0
- package/dist/rules/complete-sentences-line-comments.d.ts +4 -0
- package/dist/rules/complete-sentences-line-comments.d.ts.map +1 -0
- package/dist/rules/complete-sentences-line-comments.js +88 -0
- package/dist/rules/consistent-enum-values.d.ts +2 -0
- package/dist/rules/consistent-enum-values.d.ts.map +1 -0
- package/dist/rules/consistent-enum-values.js +46 -0
- package/dist/rules/consistent-named-tuples.d.ts +2 -0
- package/dist/rules/consistent-named-tuples.d.ts.map +1 -0
- package/dist/rules/consistent-named-tuples.js +34 -0
- package/dist/rules/eqeqeq-fix.d.ts +2 -0
- package/dist/rules/eqeqeq-fix.d.ts.map +1 -0
- package/dist/rules/eqeqeq-fix.js +173 -0
- package/dist/rules/format-jsdoc-comments.d.ts +8 -0
- package/dist/rules/format-jsdoc-comments.d.ts.map +1 -0
- package/dist/rules/format-jsdoc-comments.js +117 -0
- package/dist/rules/format-line-comments.d.ts +8 -0
- package/dist/rules/format-line-comments.d.ts.map +1 -0
- package/dist/rules/format-line-comments.js +118 -0
- package/dist/rules/jsdoc-code-block-language.d.ts +2 -0
- package/dist/rules/jsdoc-code-block-language.d.ts.map +1 -0
- package/dist/rules/jsdoc-code-block-language.js +52 -0
- package/dist/rules/newline-between-switch-case.d.ts +4 -0
- package/dist/rules/newline-between-switch-case.d.ts.map +1 -0
- package/dist/rules/newline-between-switch-case.js +63 -0
- package/dist/rules/no-confusing-set-methods.d.ts +5 -0
- package/dist/rules/no-confusing-set-methods.d.ts.map +1 -0
- package/dist/rules/no-confusing-set-methods.js +51 -0
- package/dist/rules/no-empty-jsdoc.d.ts +2 -0
- package/dist/rules/no-empty-jsdoc.d.ts.map +1 -0
- package/dist/rules/no-empty-jsdoc.js +45 -0
- package/dist/rules/no-empty-line-comments.d.ts +2 -0
- package/dist/rules/no-empty-line-comments.d.ts.map +1 -0
- package/dist/rules/no-empty-line-comments.js +40 -0
- package/dist/rules/no-explicit-array-loops.d.ts +5 -0
- package/dist/rules/no-explicit-array-loops.d.ts.map +1 -0
- package/dist/rules/no-explicit-array-loops.js +114 -0
- package/dist/rules/no-explicit-map-set-loops.d.ts +5 -0
- package/dist/rules/no-explicit-map-set-loops.d.ts.map +1 -0
- package/dist/rules/no-explicit-map-set-loops.js +74 -0
- package/dist/rules/no-for-in.d.ts +2 -0
- package/dist/rules/no-for-in.d.ts.map +1 -0
- package/dist/rules/no-for-in.js +27 -0
- package/dist/rules/no-let-any.d.ts +3 -0
- package/dist/rules/no-let-any.d.ts.map +1 -0
- package/dist/rules/no-let-any.js +45 -0
- package/dist/rules/no-mutable-return.d.ts +5 -0
- package/dist/rules/no-mutable-return.d.ts.map +1 -0
- package/dist/rules/no-mutable-return.js +63 -0
- package/dist/rules/no-number-enums.d.ts +2 -0
- package/dist/rules/no-number-enums.d.ts.map +1 -0
- package/dist/rules/no-number-enums.js +27 -0
- package/dist/rules/no-object-any.d.ts +3 -0
- package/dist/rules/no-object-any.d.ts.map +1 -0
- package/dist/rules/no-object-any.js +51 -0
- package/dist/rules/no-object-methods-with-map-set.d.ts +5 -0
- package/dist/rules/no-object-methods-with-map-set.d.ts.map +1 -0
- package/dist/rules/no-object-methods-with-map-set.js +84 -0
- package/dist/rules/no-string-length-0.d.ts +3 -0
- package/dist/rules/no-string-length-0.d.ts.map +1 -0
- package/dist/rules/no-string-length-0.js +52 -0
- package/dist/rules/no-template-curly-in-string-fix.d.ts +6 -0
- package/dist/rules/no-template-curly-in-string-fix.d.ts.map +1 -0
- package/dist/rules/no-template-curly-in-string-fix.js +39 -0
- package/dist/rules/no-undefined-return-type.d.ts +3 -0
- package/dist/rules/no-undefined-return-type.d.ts.map +1 -0
- package/dist/rules/no-undefined-return-type.js +40 -0
- package/dist/rules/no-unnecessary-assignment.d.ts +5 -0
- package/dist/rules/no-unnecessary-assignment.d.ts.map +1 -0
- package/dist/rules/no-unnecessary-assignment.js +255 -0
- package/dist/rules/no-unsafe-plusplus.d.ts +2 -0
- package/dist/rules/no-unsafe-plusplus.d.ts.map +1 -0
- package/dist/rules/no-unsafe-plusplus.js +34 -0
- package/dist/rules/no-useless-return.d.ts +2 -0
- package/dist/rules/no-useless-return.d.ts.map +1 -0
- package/dist/rules/no-useless-return.js +347 -0
- package/dist/rules/no-void-return-type.d.ts +2 -0
- package/dist/rules/no-void-return-type.d.ts.map +1 -0
- package/dist/rules/no-void-return-type.js +49 -0
- package/dist/rules/prefer-const.d.ts +2 -0
- package/dist/rules/prefer-const.d.ts.map +1 -0
- package/dist/rules/prefer-const.js +426 -0
- package/dist/rules/prefer-plusplus.d.ts +5 -0
- package/dist/rules/prefer-plusplus.d.ts.map +1 -0
- package/dist/rules/prefer-plusplus.js +49 -0
- package/dist/rules/prefer-postfix-plusplus.d.ts +2 -0
- package/dist/rules/prefer-postfix-plusplus.d.ts.map +1 -0
- package/dist/rules/prefer-postfix-plusplus.js +32 -0
- package/dist/rules/prefer-readonly-parameter-types.d.ts +13 -0
- package/dist/rules/prefer-readonly-parameter-types.d.ts.map +1 -0
- package/dist/rules/prefer-readonly-parameter-types.js +140 -0
- package/dist/rules/require-break.d.ts +5 -0
- package/dist/rules/require-break.d.ts.map +1 -0
- package/dist/rules/require-break.js +76 -0
- package/dist/rules/require-capital-const-assertions.d.ts +4 -0
- package/dist/rules/require-capital-const-assertions.d.ts.map +1 -0
- package/dist/rules/require-capital-const-assertions.js +112 -0
- package/dist/rules/require-capital-read-only.d.ts +5 -0
- package/dist/rules/require-capital-read-only.d.ts.map +1 -0
- package/dist/rules/require-capital-read-only.js +111 -0
- package/dist/rules/require-unannotated-const-assertions.d.ts +2 -0
- package/dist/rules/require-unannotated-const-assertions.d.ts.map +1 -0
- package/dist/rules/require-unannotated-const-assertions.js +27 -0
- package/dist/rules/require-variadic-function-argument.d.ts +5 -0
- package/dist/rules/require-variadic-function-argument.d.ts.map +1 -0
- package/dist/rules/require-variadic-function-argument.js +86 -0
- package/dist/rules/strict-array-methods.d.ts +3 -0
- package/dist/rules/strict-array-methods.d.ts.map +1 -0
- package/dist/rules/strict-array-methods.js +83 -0
- package/dist/rules/strict-enums.d.ts +5 -0
- package/dist/rules/strict-enums.d.ts.map +1 -0
- package/dist/rules/strict-enums.js +445 -0
- package/dist/rules/strict-undefined-functions.d.ts +5 -0
- package/dist/rules/strict-undefined-functions.d.ts.map +1 -0
- package/dist/rules/strict-undefined-functions.js +49 -0
- package/dist/rules/strict-void-functions.d.ts +2 -0
- package/dist/rules/strict-void-functions.d.ts.map +1 -0
- package/dist/rules/strict-void-functions.js +43 -0
- package/dist/rules.d.ts +49 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +85 -0
- package/dist/template.d.ts +2 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +29 -0
- package/dist/typeUtils.d.ts +28 -0
- package/dist/typeUtils.d.ts.map +1 -0
- package/dist/typeUtils.js +76 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +54 -0
- package/package.json +55 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import { capitalizeFirstLetter, trimPrefix } from "../completeCommon.js";
|
|
3
|
+
import { getTypeName } from "../typeUtils.js";
|
|
4
|
+
import { createRule } from "../utils.js";
|
|
5
|
+
const PROBLEM_METHODS = new Set([
|
|
6
|
+
"entries",
|
|
7
|
+
"keys",
|
|
8
|
+
"values",
|
|
9
|
+
]);
|
|
10
|
+
const PROBLEM_TYPES = new Set([
|
|
11
|
+
"Map",
|
|
12
|
+
"Set",
|
|
13
|
+
"ReadonlyMap",
|
|
14
|
+
"ReadonlySet",
|
|
15
|
+
]);
|
|
16
|
+
export const noObjectMethodsWithMapSet = createRule({
|
|
17
|
+
name: "no-object-methods-with-map-set",
|
|
18
|
+
meta: {
|
|
19
|
+
type: "problem",
|
|
20
|
+
docs: {
|
|
21
|
+
description: "Disallows using object methods with maps and sets",
|
|
22
|
+
recommended: true,
|
|
23
|
+
requiresTypeChecking: true,
|
|
24
|
+
},
|
|
25
|
+
schema: [],
|
|
26
|
+
messages: {
|
|
27
|
+
noObjectEntriesMap: "You cannot use the `Object.entries` method with a map. Consider using the `entries` method on the map instead.",
|
|
28
|
+
noObjectKeysMap: "You cannot use the `Object.keys` method with a map. Consider using the `keys` method on the map instead.",
|
|
29
|
+
noObjectValuesMap: "You cannot use the `Object.values` method with a map. Consider using the `values` method on the map instead.",
|
|
30
|
+
noObjectEntriesSet: "You cannot use the `Object.entries` method with a set. Consider using the `entries` method on the set instead.",
|
|
31
|
+
noObjectKeysSet: "You cannot use the `Object.keys` method with a set. Consider using the `keys` method on the set instead.",
|
|
32
|
+
noObjectValuesSet: "You cannot use the `Object.values` method with a set. Consider using the `values` method on the set instead.",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultOptions: [],
|
|
36
|
+
create(context) {
|
|
37
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
38
|
+
const checker = parserServices.program.getTypeChecker();
|
|
39
|
+
return {
|
|
40
|
+
/** When a function or method is called. */
|
|
41
|
+
CallExpression(node) {
|
|
42
|
+
// First, check if this is invoking a method on an object.
|
|
43
|
+
const { callee } = node;
|
|
44
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Second, check if the object is `Object`.
|
|
48
|
+
const { object } = callee;
|
|
49
|
+
if (object.type !== AST_NODE_TYPES.Identifier ||
|
|
50
|
+
object.name !== "Object") {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Third, check if this is one of the problem methods.
|
|
54
|
+
const { property } = callee;
|
|
55
|
+
if (property.type !== AST_NODE_TYPES.Identifier ||
|
|
56
|
+
!PROBLEM_METHODS.has(property.name)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const methodName = property.name;
|
|
60
|
+
// Fourth, check the type of the thing being passed.
|
|
61
|
+
const firstArgument = node.arguments[0];
|
|
62
|
+
if (firstArgument === undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(firstArgument);
|
|
66
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
67
|
+
const typeName = getTypeName(type);
|
|
68
|
+
if (typeName === undefined) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!PROBLEM_TYPES.has(typeName)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const capitalMethodName = capitalizeFirstLetter(methodName);
|
|
75
|
+
const trimmedTypeName = trimPrefix(typeName, "Readonly");
|
|
76
|
+
const messageId = `noObject${capitalMethodName}${trimmedTypeName}`;
|
|
77
|
+
context.report({
|
|
78
|
+
loc: node.loc,
|
|
79
|
+
messageId,
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-string-length-0.d.ts","sourceRoot":"","sources":["../../src/rules/no-string-length-0.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAKvE,eAAO,MAAM,eAAe,+HAyD1B,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { isFlagSet } from "../typeUtils.js";
|
|
4
|
+
import { createRule } from "../utils.js";
|
|
5
|
+
export const noStringLength0 = createRule({
|
|
6
|
+
name: "no-string-length-0",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Disallows checking for empty strings via the length method in favor of direct comparison to an empty string",
|
|
11
|
+
recommended: true,
|
|
12
|
+
requiresTypeChecking: true,
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
noStringLength0: "Compare to an empty string directly instead of using the length method, as it is shorter and easier to read.",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultOptions: [],
|
|
20
|
+
create(context) {
|
|
21
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
22
|
+
const checker = parserServices.program.getTypeChecker();
|
|
23
|
+
function isStringLength(node) {
|
|
24
|
+
if (node.type !== AST_NODE_TYPES.MemberExpression) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (node.property.type !== AST_NODE_TYPES.Identifier ||
|
|
28
|
+
node.property.name !== "length") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.object);
|
|
32
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
33
|
+
// We don't use `isTypeFlagSet` because we don't want to iterate through any unions; we only
|
|
34
|
+
// want to match exactly `string`.
|
|
35
|
+
return isFlagSet(type.flags, ts.TypeFlags.String);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
BinaryExpression(node) {
|
|
39
|
+
if ((is0(node.left) && isStringLength(node.right)) ||
|
|
40
|
+
(is0(node.right) && isStringLength(node.left))) {
|
|
41
|
+
context.report({
|
|
42
|
+
node,
|
|
43
|
+
messageId: "noStringLength0",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
function is0(node) {
|
|
51
|
+
return node.type === AST_NODE_TYPES.Literal && node.raw === "0";
|
|
52
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This rule is slightly modified from the original ESLint version:
|
|
3
|
+
* https://github.com/eslint/eslint/blob/main/lib/rules/no-template-curly-in-string.js
|
|
4
|
+
*/
|
|
5
|
+
export declare const noTemplateCurlyInStringFix: import("@typescript-eslint/utils/ts-eslint").RuleModule<"unexpectedTemplateExpression", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
6
|
+
//# sourceMappingURL=no-template-curly-in-string-fix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-template-curly-in-string-fix.d.ts","sourceRoot":"","sources":["../../src/rules/no-template-curly-in-string-fix.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,eAAO,MAAM,0BAA0B,8MAmCrC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This rule is slightly modified from the original ESLint version:
|
|
3
|
+
* https://github.com/eslint/eslint/blob/main/lib/rules/no-template-curly-in-string.js
|
|
4
|
+
*/
|
|
5
|
+
import { createRule } from "../utils.js";
|
|
6
|
+
const ERRONEOUS_TEMPLATE_STRING_REGEX = /\$\{[^}]+\}/u;
|
|
7
|
+
export const noTemplateCurlyInStringFix = createRule({
|
|
8
|
+
name: "no-template-curly-in-string-fix",
|
|
9
|
+
meta: {
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
description: "Disallows template literal placeholder syntax in regular strings (and automatically fixes)",
|
|
13
|
+
recommended: true,
|
|
14
|
+
requiresTypeChecking: false,
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
18
|
+
unexpectedTemplateExpression: "Unexpected template string expression.",
|
|
19
|
+
},
|
|
20
|
+
fixable: "code",
|
|
21
|
+
},
|
|
22
|
+
defaultOptions: [],
|
|
23
|
+
create(context) {
|
|
24
|
+
return {
|
|
25
|
+
Literal(node) {
|
|
26
|
+
if (typeof node.value === "string" &&
|
|
27
|
+
ERRONEOUS_TEMPLATE_STRING_REGEX.test(node.value)) {
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: "unexpectedTemplateExpression",
|
|
31
|
+
fix(fixer) {
|
|
32
|
+
return fixer.replaceText(node, `\`${node.value}\``);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
export declare const noUndefinedReturnType: ESLintUtils.RuleModule<"undefinedReturnType", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, ESLintUtils.RuleListener>;
|
|
3
|
+
//# sourceMappingURL=no-undefined-return-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-undefined-return-type.d.ts","sourceRoot":"","sources":["../../src/rules/no-undefined-return-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIvD,eAAO,MAAM,qBAAqB,mIAsChC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import { getTypeName } from "../typeUtils.js";
|
|
3
|
+
import { createRule } from "../utils.js";
|
|
4
|
+
export const noUndefinedReturnType = createRule({
|
|
5
|
+
name: "no-undefined-return-type",
|
|
6
|
+
meta: {
|
|
7
|
+
type: "problem",
|
|
8
|
+
docs: {
|
|
9
|
+
description: "Disallows `undefined` return types on functions",
|
|
10
|
+
recommended: true,
|
|
11
|
+
requiresTypeChecking: true,
|
|
12
|
+
},
|
|
13
|
+
schema: [],
|
|
14
|
+
messages: {
|
|
15
|
+
undefinedReturnType: "Functions cannot have an `undefined` return type. Change the return type to `void` instead.",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
21
|
+
const checker = parserServices.program.getTypeChecker();
|
|
22
|
+
return {
|
|
23
|
+
FunctionDeclaration(node) {
|
|
24
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
25
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
26
|
+
const signatures = type.getCallSignatures();
|
|
27
|
+
for (const signature of signatures) {
|
|
28
|
+
const returnType = signature.getReturnType();
|
|
29
|
+
const typeName = getTypeName(returnType);
|
|
30
|
+
if (typeName === "undefined") {
|
|
31
|
+
context.report({
|
|
32
|
+
loc: node.loc,
|
|
33
|
+
messageId: "undefinedReturnType",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
export type Options = [];
|
|
3
|
+
export type MessageIds = "unnecessary" | "unnecessaryZero" | "unnecessaryEmptyString" | "unnecessaryShortCircuit";
|
|
4
|
+
export declare const noUnnecessaryAssignment: ESLintUtils.RuleModule<MessageIds, [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, ESLintUtils.RuleListener>;
|
|
5
|
+
//# sourceMappingURL=no-unnecessary-assignment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unnecessary-assignment.d.ts","sourceRoot":"","sources":["../../src/rules/no-unnecessary-assignment.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAmBvE,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAClB,aAAa,GACb,iBAAiB,GACjB,wBAAwB,GACxB,yBAAyB,CAAC;AAE9B,eAAO,MAAM,uBAAuB,wHA2SlC,CAAC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { isFlagSet, unionTypeParts } from "../typeUtils.js";
|
|
4
|
+
import { createRule } from "../utils.js";
|
|
5
|
+
const USELESS_OPERATORS_WITH_ZERO = new Set([
|
|
6
|
+
"+",
|
|
7
|
+
"-",
|
|
8
|
+
"|",
|
|
9
|
+
// "<<" is technically a useless operator, but we whitelist it so that bit flag enums will not
|
|
10
|
+
// cause false positives.
|
|
11
|
+
">>",
|
|
12
|
+
">>>",
|
|
13
|
+
]);
|
|
14
|
+
const USELESS_ASSIGNMENT_OPERATORS_WITH_ZERO = new Set([...USELESS_OPERATORS_WITH_ZERO].map((value) => `${value}=`));
|
|
15
|
+
export const noUnnecessaryAssignment = createRule({
|
|
16
|
+
name: "no-unnecessary-assignment",
|
|
17
|
+
meta: {
|
|
18
|
+
type: "problem",
|
|
19
|
+
docs: {
|
|
20
|
+
description: "Disallows useless assignments",
|
|
21
|
+
recommended: true,
|
|
22
|
+
requiresTypeChecking: true,
|
|
23
|
+
},
|
|
24
|
+
schema: [],
|
|
25
|
+
messages: {
|
|
26
|
+
unnecessary: "This assignment is unnecessary because it would have no effect on the variable.",
|
|
27
|
+
unnecessaryZero: 'This assignment is unnecessary because the "{{ operator }}" operator in combination with 0 would have no effect on a number variable.',
|
|
28
|
+
unnecessaryEmptyString: "This assignment is unnecessary because adding an empty string would have no effect on a string variable.",
|
|
29
|
+
unnecessaryShortCircuit: 'This short circuit assignment using the "{{operator}}" operator is unnecessary because it would have no effect on the variable.',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultOptions: [],
|
|
33
|
+
create(context) {
|
|
34
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
35
|
+
const checker = parserServices.program.getTypeChecker();
|
|
36
|
+
function nodeHasFlagSet(node, flag) {
|
|
37
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
38
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
39
|
+
// We cannot use `isTypeFlagSet` because we do not want to penetrate unions.
|
|
40
|
+
return isFlagSet(type.flags, flag);
|
|
41
|
+
}
|
|
42
|
+
function isBoolean(node) {
|
|
43
|
+
return nodeHasFlagSet(node, ts.TypeFlags.BooleanLike);
|
|
44
|
+
}
|
|
45
|
+
function isNumber(node) {
|
|
46
|
+
return nodeHasFlagSet(node, ts.TypeFlags.NumberLike);
|
|
47
|
+
}
|
|
48
|
+
function isString(node) {
|
|
49
|
+
return nodeHasFlagSet(node, ts.TypeFlags.StringLike);
|
|
50
|
+
}
|
|
51
|
+
function isNull(node) {
|
|
52
|
+
return nodeHasFlagSet(node, ts.TypeFlags.Null);
|
|
53
|
+
}
|
|
54
|
+
function isUndefined(node) {
|
|
55
|
+
return nodeHasFlagSet(node, ts.TypeFlags.Undefined);
|
|
56
|
+
}
|
|
57
|
+
function isTrue(node) {
|
|
58
|
+
// Checking for the literal value is fast, so we do that first.
|
|
59
|
+
if (node.type === AST_NODE_TYPES.Literal && node.value === true) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
63
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
64
|
+
return "intrinsicName" in type && type.intrinsicName === "true";
|
|
65
|
+
}
|
|
66
|
+
function isFalse(node) {
|
|
67
|
+
// Checking for the literal value is fast, so we do that first.
|
|
68
|
+
if (node.type === AST_NODE_TYPES.Literal && node.value === false) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
72
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
73
|
+
return "intrinsicName" in type && type.intrinsicName === "false";
|
|
74
|
+
}
|
|
75
|
+
function isZero(node) {
|
|
76
|
+
// Checking for the literal value is fast, so we do that first.
|
|
77
|
+
if (isLiteralZero(node)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
81
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
82
|
+
return "value" in type && type.value === 0;
|
|
83
|
+
}
|
|
84
|
+
function isEmptyString(node) {
|
|
85
|
+
// Checking for the literal value is fast, so we do that first.
|
|
86
|
+
if (isLiteralEmptyString(node)) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
90
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
91
|
+
return "value" in type && type.value === "";
|
|
92
|
+
}
|
|
93
|
+
function hasNullAndNotUndefined(node) {
|
|
94
|
+
if (isNull(node)) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
98
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
99
|
+
const types = unionTypeParts(type);
|
|
100
|
+
return (types.some((t) => isFlagSet(t.flags, ts.TypeFlags.Null)) &&
|
|
101
|
+
!types.some((t) => isFlagSet(t.flags, ts.TypeFlags.Undefined)));
|
|
102
|
+
}
|
|
103
|
+
function hasUndefinedAndNotNull(node) {
|
|
104
|
+
if (isUndefined(node)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
108
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
109
|
+
const types = unionTypeParts(type);
|
|
110
|
+
return (types.some((t) => isFlagSet(t.flags, ts.TypeFlags.Undefined)) &&
|
|
111
|
+
!types.some((t) => isFlagSet(t.flags, ts.TypeFlags.Null)));
|
|
112
|
+
}
|
|
113
|
+
function checkEqualSign(node) {
|
|
114
|
+
const leftTSNode = parserServices.esTreeNodeToTSNodeMap.get(node.left);
|
|
115
|
+
const rightTSNode = parserServices.esTreeNodeToTSNodeMap.get(node.right);
|
|
116
|
+
const leftType = checker.getTypeAtLocation(leftTSNode);
|
|
117
|
+
const rightType = checker.getTypeAtLocation(rightTSNode);
|
|
118
|
+
const isLeftLiteral = isFlagSet(leftType.flags, ts.TypeFlags.Literal);
|
|
119
|
+
const isRightLiteral = isFlagSet(rightType.flags, ts.TypeFlags.Literal);
|
|
120
|
+
if (isLeftLiteral && isRightLiteral && leftType === rightType) {
|
|
121
|
+
context.report({
|
|
122
|
+
loc: node.loc,
|
|
123
|
+
messageId: "unnecessary",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
AssignmentExpression(node) {
|
|
129
|
+
if (node.operator === "=") {
|
|
130
|
+
checkEqualSign(node);
|
|
131
|
+
}
|
|
132
|
+
else if (USELESS_ASSIGNMENT_OPERATORS_WITH_ZERO.has(node.operator) &&
|
|
133
|
+
isNumber(node.left) &&
|
|
134
|
+
isLiteralZero(node.right)) {
|
|
135
|
+
context.report({
|
|
136
|
+
loc: node.loc,
|
|
137
|
+
messageId: "unnecessaryZero",
|
|
138
|
+
data: {
|
|
139
|
+
operator: node.operator,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else if (node.operator === "+=" &&
|
|
144
|
+
isString(node.left) &&
|
|
145
|
+
isLiteralEmptyString(node.right)) {
|
|
146
|
+
context.report({
|
|
147
|
+
loc: node.loc,
|
|
148
|
+
messageId: "unnecessaryEmptyString",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
BinaryExpression(node) {
|
|
153
|
+
if (USELESS_OPERATORS_WITH_ZERO.has(node.operator) &&
|
|
154
|
+
((isNumber(node.left) && isLiteralZero(node.right)) ||
|
|
155
|
+
(isNumber(node.right) && isLiteralZero(node.left)))) {
|
|
156
|
+
context.report({
|
|
157
|
+
loc: node.loc,
|
|
158
|
+
messageId: "unnecessaryZero",
|
|
159
|
+
data: {
|
|
160
|
+
operator: node.operator,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Plus is the only operator valid for strings.
|
|
165
|
+
if (node.operator === "+" &&
|
|
166
|
+
((isString(node.left) && isLiteralEmptyString(node.right)) ||
|
|
167
|
+
(isString(node.right) && isLiteralEmptyString(node.left)))) {
|
|
168
|
+
context.report({
|
|
169
|
+
loc: node.loc,
|
|
170
|
+
messageId: "unnecessaryEmptyString",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
LogicalExpression(node) {
|
|
175
|
+
const { parent } = node;
|
|
176
|
+
if (parent.type !== AST_NODE_TYPES.AssignmentExpression &&
|
|
177
|
+
parent.type !== AST_NODE_TYPES.VariableDeclarator) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (node.operator === "||" &&
|
|
181
|
+
isBoolean(node.left) &&
|
|
182
|
+
isFalse(node.right)) {
|
|
183
|
+
context.report({
|
|
184
|
+
loc: node.loc,
|
|
185
|
+
messageId: "unnecessaryShortCircuit",
|
|
186
|
+
data: {
|
|
187
|
+
operator: node.operator,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (node.operator === "&&" &&
|
|
192
|
+
isBoolean(node.left) &&
|
|
193
|
+
isTrue(node.right)) {
|
|
194
|
+
context.report({
|
|
195
|
+
loc: node.loc,
|
|
196
|
+
messageId: "unnecessaryShortCircuit",
|
|
197
|
+
data: {
|
|
198
|
+
operator: node.operator,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (node.operator === "||" &&
|
|
203
|
+
isNumber(node.left) &&
|
|
204
|
+
isZero(node.right)) {
|
|
205
|
+
context.report({
|
|
206
|
+
loc: node.loc,
|
|
207
|
+
messageId: "unnecessaryShortCircuit",
|
|
208
|
+
data: {
|
|
209
|
+
operator: node.operator,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (node.operator === "||" &&
|
|
214
|
+
isString(node.left) &&
|
|
215
|
+
isEmptyString(node.right)) {
|
|
216
|
+
context.report({
|
|
217
|
+
loc: node.loc,
|
|
218
|
+
messageId: "unnecessaryShortCircuit",
|
|
219
|
+
data: {
|
|
220
|
+
operator: node.operator,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (node.operator === "??" &&
|
|
225
|
+
hasNullAndNotUndefined(node.left) &&
|
|
226
|
+
isNull(node.right)) {
|
|
227
|
+
context.report({
|
|
228
|
+
loc: node.loc,
|
|
229
|
+
messageId: "unnecessaryShortCircuit",
|
|
230
|
+
data: {
|
|
231
|
+
operator: node.operator,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (node.operator === "??" &&
|
|
236
|
+
hasUndefinedAndNotNull(node.left) &&
|
|
237
|
+
isUndefined(node.right)) {
|
|
238
|
+
context.report({
|
|
239
|
+
loc: node.loc,
|
|
240
|
+
messageId: "unnecessaryShortCircuit",
|
|
241
|
+
data: {
|
|
242
|
+
operator: node.operator,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
function isLiteralZero(node) {
|
|
251
|
+
return node.type === AST_NODE_TYPES.Literal && node.value === 0;
|
|
252
|
+
}
|
|
253
|
+
function isLiteralEmptyString(node) {
|
|
254
|
+
return node.type === AST_NODE_TYPES.Literal && node.raw === '""';
|
|
255
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const noUnsafePlusplus: import("@typescript-eslint/utils/ts-eslint").RuleModule<"plusPlus" | "minusMinus", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
2
|
+
//# sourceMappingURL=no-unsafe-plusplus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unsafe-plusplus.d.ts","sourceRoot":"","sources":["../../src/rules/no-unsafe-plusplus.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,yMAwC3B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createRule } from "../utils.js";
|
|
2
|
+
export const noUnsafePlusplus = createRule({
|
|
3
|
+
name: "no-unsafe-plusplus",
|
|
4
|
+
meta: {
|
|
5
|
+
type: "problem",
|
|
6
|
+
docs: {
|
|
7
|
+
description: 'Disallow unsafe and confusing uses of the "++" and "--" operators',
|
|
8
|
+
recommended: true,
|
|
9
|
+
requiresTypeChecking: true,
|
|
10
|
+
},
|
|
11
|
+
schema: [],
|
|
12
|
+
messages: {
|
|
13
|
+
plusPlus: 'Complex usages of the "++" operator can be confusing. You should only use it in contexts where it could be swapped with "+= 1".',
|
|
14
|
+
minusMinus: 'Complex usages of the "--" operator can be confusing. You should only use it in contexts where it could be swapped with "-= 1".',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultOptions: [],
|
|
18
|
+
create(context) {
|
|
19
|
+
function unsafe(nodeNever) {
|
|
20
|
+
// The type of the node is "never" for some reason, so cast it to something we can work with.
|
|
21
|
+
const node = nodeNever;
|
|
22
|
+
const { operator } = node;
|
|
23
|
+
const messageId = operator === "--" ? "minusMinus" : "plusPlus";
|
|
24
|
+
context.report({
|
|
25
|
+
loc: node.loc,
|
|
26
|
+
messageId,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
":not(UnaryExpression[operator='void'],ExpressionStatement,SequenceExpression,ForStatement[update.type=UpdateExpression]) > UpdateExpression[prefix='false'], ForStatement[init.type=UpdateExpression] > UpdateExpression[prefix='false'], ForStatement[test.type=UpdateExpression] > UpdateExpression[prefix='false']": unsafe,
|
|
31
|
+
"SequenceExpression > UpdateExpression[prefix='false']:last-child": unsafe,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const noUselessReturn: import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnecessaryReturn", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
2
|
+
//# sourceMappingURL=no-useless-return.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-useless-return.d.ts","sourceRoot":"","sources":["../../src/rules/no-useless-return.ts"],"names":[],"mappings":"AA6IA,eAAO,MAAM,eAAe,mMAoR1B,CAAC"}
|