@blumintinc/eslint-plugin-blumint 1.7.2 → 1.8.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/README.md +39 -62
- package/lib/index.js +37 -1
- package/lib/rules/enforce-assert-throws.js +48 -3
- package/lib/rules/enforce-assertSafe-object-key.d.ts +2 -0
- package/lib/rules/enforce-assertSafe-object-key.js +284 -0
- package/lib/rules/enforce-centralized-mock-firestore.js +193 -15
- package/lib/rules/enforce-firestore-facade.js +8 -0
- package/lib/rules/enforce-microdiff.d.ts +3 -0
- package/lib/rules/enforce-microdiff.js +379 -0
- package/lib/rules/enforce-mock-firestore.js +2 -2
- package/lib/rules/enforce-object-literal-as-const.d.ts +4 -0
- package/lib/rules/enforce-object-literal-as-const.js +88 -0
- package/lib/rules/enforce-positive-naming.d.ts +1 -0
- package/lib/rules/enforce-positive-naming.js +363 -0
- package/lib/rules/enforce-props-argument-name.d.ts +8 -0
- package/lib/rules/enforce-props-argument-name.js +182 -0
- package/lib/rules/enforce-render-hits-memoization.js +166 -17
- package/lib/rules/enforce-timestamp-now.d.ts +1 -0
- package/lib/rules/enforce-timestamp-now.js +223 -0
- package/lib/rules/enforce-verb-noun-naming.js +1 -0
- package/lib/rules/extract-global-constants.d.ts +1 -1
- package/lib/rules/extract-global-constants.js +109 -1
- package/lib/rules/global-const-style.js +3 -2
- package/lib/rules/no-always-true-false-conditions.d.ts +3 -0
- package/lib/rules/no-always-true-false-conditions.js +1202 -0
- package/lib/rules/no-firestore-jest-mock.js +27 -4
- package/lib/rules/no-mock-firebase-admin.js +21 -8
- package/lib/rules/no-type-assertion-returns.d.ts +9 -0
- package/lib/rules/no-type-assertion-returns.js +289 -0
- package/lib/rules/no-unnecessary-verb-suffix.d.ts +1 -0
- package/lib/rules/no-unnecessary-verb-suffix.js +203 -0
- package/lib/rules/no-unused-props.js +47 -1
- package/lib/rules/prefer-clone-deep.js +294 -22
- package/lib/rules/prefer-fragment-component.js +265 -34
- package/lib/rules/prefer-global-router-state-key.d.ts +5 -0
- package/lib/rules/prefer-global-router-state-key.js +89 -0
- package/lib/rules/prefer-utility-function-over-private-static.d.ts +1 -0
- package/lib/rules/prefer-utility-function-over-private-static.js +77 -0
- package/lib/rules/react-usememo-should-be-component.d.ts +1 -0
- package/lib/rules/react-usememo-should-be-component.js +256 -0
- package/package.json +5 -5
|
@@ -26,14 +26,26 @@ exports.noFirestoreJestMock = (0, createRule_1.createRule)({
|
|
|
26
26
|
}
|
|
27
27
|
return {
|
|
28
28
|
ImportDeclaration(node) {
|
|
29
|
+
// Skip type imports completely
|
|
30
|
+
if (node.importKind === 'type') {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
29
33
|
if (node.source.value === 'firestore-jest-mock') {
|
|
30
34
|
context.report({
|
|
31
35
|
node,
|
|
32
36
|
messageId: 'noFirestoreJestMock',
|
|
33
|
-
fix(fixer) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
fix: (fixer) => {
|
|
38
|
+
return fixer.replaceText(node, `import { mockFirestore } from '../../../../../__test-utils__/mockFirestore';`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
ImportExpression(node) {
|
|
44
|
+
if (node.source.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
45
|
+
node.source.value === 'firestore-jest-mock') {
|
|
46
|
+
context.report({
|
|
47
|
+
node,
|
|
48
|
+
messageId: 'noFirestoreJestMock',
|
|
37
49
|
});
|
|
38
50
|
}
|
|
39
51
|
},
|
|
@@ -52,6 +64,17 @@ exports.noFirestoreJestMock = (0, createRule_1.createRule)({
|
|
|
52
64
|
messageId: 'noFirestoreJestMock',
|
|
53
65
|
});
|
|
54
66
|
}
|
|
67
|
+
// Check for require('firestore-jest-mock')
|
|
68
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
69
|
+
node.callee.name === 'require' &&
|
|
70
|
+
node.arguments.length > 0 &&
|
|
71
|
+
node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal &&
|
|
72
|
+
node.arguments[0].value === 'firestore-jest-mock') {
|
|
73
|
+
context.report({
|
|
74
|
+
node,
|
|
75
|
+
messageId: 'noFirestoreJestMock',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
55
78
|
},
|
|
56
79
|
};
|
|
57
80
|
},
|
|
@@ -4,9 +4,9 @@ exports.noMockFirebaseAdmin = void 0;
|
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
6
|
const FIREBASE_ADMIN_PATHS = [
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
7
|
+
'firebaseAdmin',
|
|
8
|
+
'config/firebaseAdmin',
|
|
9
|
+
'src/config/firebaseAdmin',
|
|
10
10
|
'functions/src/config/firebaseAdmin',
|
|
11
11
|
];
|
|
12
12
|
exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
|
|
@@ -19,11 +19,18 @@ exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
|
|
|
19
19
|
},
|
|
20
20
|
schema: [],
|
|
21
21
|
messages: {
|
|
22
|
-
noMockFirebaseAdmin: 'Do not mock firebaseAdmin directly. Use mockFirestore from
|
|
22
|
+
noMockFirebaseAdmin: 'Do not mock firebaseAdmin directly. Use mockFirestore from __test-utils__/mockFirestore instead.',
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
defaultOptions: [],
|
|
26
26
|
create(context) {
|
|
27
|
+
// Check if the current file is a test file
|
|
28
|
+
const filename = context.getFilename();
|
|
29
|
+
const isTestFile = /\.test\.[jt]sx?$/.test(filename) || /\.spec\.[jt]sx?$/.test(filename);
|
|
30
|
+
// If it's not a test file, don't apply the rule
|
|
31
|
+
if (!isTestFile) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
27
34
|
return {
|
|
28
35
|
CallExpression(node) {
|
|
29
36
|
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
@@ -32,10 +39,16 @@ exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
|
|
|
32
39
|
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
33
40
|
node.callee.property.name === 'mock' &&
|
|
34
41
|
node.arguments.length > 0 &&
|
|
35
|
-
node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
(node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal ||
|
|
43
|
+
node.arguments[0].type === utils_1.AST_NODE_TYPES.TemplateLiteral)) {
|
|
44
|
+
let mockPath = '';
|
|
45
|
+
if (node.arguments[0].type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
|
|
46
|
+
mockPath = node.arguments[0].quasis[0].value.raw;
|
|
47
|
+
}
|
|
48
|
+
else if (node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal) {
|
|
49
|
+
mockPath = String(node.arguments[0].value);
|
|
50
|
+
}
|
|
51
|
+
const isFirebaseAdminMock = FIREBASE_ADMIN_PATHS.some(path => mockPath.endsWith(path) && !mockPath.endsWith('Helper') && !mockPath.endsWith('utils') && !mockPath.endsWith('test') && !mockPath.endsWith('mock') && !mockPath.endsWith('jest-mock'));
|
|
39
52
|
if (isFirebaseAdminMock) {
|
|
40
53
|
context.report({
|
|
41
54
|
node,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type MessageIds = 'noTypeAssertionReturns' | 'useExplicitVariable';
|
|
2
|
+
type Options = [
|
|
3
|
+
{
|
|
4
|
+
allowAsConst?: boolean;
|
|
5
|
+
allowTypePredicates?: boolean;
|
|
6
|
+
}
|
|
7
|
+
];
|
|
8
|
+
export declare const noTypeAssertionReturns: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noTypeAssertionReturns = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const defaultOptions = {
|
|
7
|
+
allowAsConst: true,
|
|
8
|
+
allowTypePredicates: true,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Checks if a type assertion is 'as const'
|
|
12
|
+
*/
|
|
13
|
+
function isAsConstAssertion(node) {
|
|
14
|
+
const typeAnnotation = node.typeAnnotation;
|
|
15
|
+
return (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
16
|
+
typeAnnotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
17
|
+
typeAnnotation.typeName.name === 'const');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Checks if a node is a type predicate (using 'is' keyword)
|
|
21
|
+
*/
|
|
22
|
+
function isTypePredicate(node) {
|
|
23
|
+
return node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypePredicate;
|
|
24
|
+
}
|
|
25
|
+
exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
26
|
+
name: 'no-type-assertion-returns',
|
|
27
|
+
meta: {
|
|
28
|
+
type: 'suggestion',
|
|
29
|
+
docs: {
|
|
30
|
+
description: 'Enforce typing variables before returning them, rather than using type assertions or explicit return types',
|
|
31
|
+
recommended: 'error',
|
|
32
|
+
requiresTypeChecking: false,
|
|
33
|
+
},
|
|
34
|
+
fixable: 'code',
|
|
35
|
+
schema: [
|
|
36
|
+
{
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
allowAsConst: { type: 'boolean' },
|
|
40
|
+
allowTypePredicates: { type: 'boolean' },
|
|
41
|
+
},
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
messages: {
|
|
46
|
+
noTypeAssertionReturns: 'Type assertions in return statements are not allowed. Type the variable explicitly before returning it.',
|
|
47
|
+
useExplicitVariable: 'Explicit return type annotations are not allowed. Type the variable explicitly before returning it.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
defaultOptions: [defaultOptions],
|
|
51
|
+
create(context, [options]) {
|
|
52
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
53
|
+
return {
|
|
54
|
+
// Check for return statements with type assertions
|
|
55
|
+
ReturnStatement(node) {
|
|
56
|
+
if (!node.argument)
|
|
57
|
+
return;
|
|
58
|
+
// Check for type assertions using 'as' keyword
|
|
59
|
+
if (node.argument.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
|
|
60
|
+
// Allow 'as const' assertions if configured
|
|
61
|
+
if (mergedOptions.allowAsConst && isAsConstAssertion(node.argument)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// For nested type assertions, only report the outermost one
|
|
65
|
+
if (node.argument.expression.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
|
|
66
|
+
context.report({
|
|
67
|
+
node: node.argument,
|
|
68
|
+
messageId: 'noTypeAssertionReturns',
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
context.report({
|
|
73
|
+
node: node.argument,
|
|
74
|
+
messageId: 'noTypeAssertionReturns',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Check for type assertions using angle bracket syntax
|
|
78
|
+
if (node.argument.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
79
|
+
context.report({
|
|
80
|
+
node: node.argument,
|
|
81
|
+
messageId: 'noTypeAssertionReturns',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
// Check functions with explicit return types
|
|
86
|
+
FunctionDeclaration(node) {
|
|
87
|
+
if (!node.returnType)
|
|
88
|
+
return;
|
|
89
|
+
// Allow type predicates if configured
|
|
90
|
+
if (mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// If type predicates are not allowed, report them
|
|
94
|
+
if (!mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
95
|
+
context.report({
|
|
96
|
+
node: node.returnType,
|
|
97
|
+
messageId: 'useExplicitVariable',
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Check if the function has a return statement with a direct value (not a variable)
|
|
102
|
+
if (node.body && node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
103
|
+
for (const statement of node.body.body) {
|
|
104
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
105
|
+
// If returning a variable reference, that's fine
|
|
106
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
// If returning an object literal, array literal, or other complex expression
|
|
110
|
+
// without first assigning it to a typed variable, that's a problem
|
|
111
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
112
|
+
statement.argument.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
113
|
+
statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
114
|
+
context.report({
|
|
115
|
+
node: node.returnType,
|
|
116
|
+
messageId: 'useExplicitVariable',
|
|
117
|
+
});
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
// Check function expressions with explicit return types
|
|
125
|
+
FunctionExpression(node) {
|
|
126
|
+
if (!node.returnType)
|
|
127
|
+
return;
|
|
128
|
+
// Allow type predicates if configured
|
|
129
|
+
if (mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// If type predicates are not allowed, report them
|
|
133
|
+
if (!mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
134
|
+
context.report({
|
|
135
|
+
node: node.returnType,
|
|
136
|
+
messageId: 'useExplicitVariable',
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Check if the function has a return statement with a direct value (not a variable)
|
|
141
|
+
if (node.body && node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
142
|
+
for (const statement of node.body.body) {
|
|
143
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
144
|
+
// If returning a variable reference, that's fine
|
|
145
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// If returning an object literal, array literal, or other complex expression
|
|
149
|
+
// without first assigning it to a typed variable, that's a problem
|
|
150
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
151
|
+
statement.argument.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
152
|
+
statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
153
|
+
context.report({
|
|
154
|
+
node: node.returnType,
|
|
155
|
+
messageId: 'useExplicitVariable',
|
|
156
|
+
});
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
// Check arrow functions
|
|
164
|
+
ArrowFunctionExpression(node) {
|
|
165
|
+
// For arrow functions with expression bodies (no block)
|
|
166
|
+
if (node.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
167
|
+
// Check for explicit return type
|
|
168
|
+
if (node.returnType) {
|
|
169
|
+
// Allow type predicates if configured
|
|
170
|
+
if (mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// If type predicates are not allowed, report them
|
|
174
|
+
if (!mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
175
|
+
context.report({
|
|
176
|
+
node: node.returnType,
|
|
177
|
+
messageId: 'useExplicitVariable',
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// If returning an object literal with explicit return type, report it
|
|
182
|
+
if (node.body.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
183
|
+
node.body.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
184
|
+
context.report({
|
|
185
|
+
node: node.returnType,
|
|
186
|
+
messageId: 'useExplicitVariable',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Check for type assertions in the body
|
|
191
|
+
if (node.body.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
|
|
192
|
+
// Allow 'as const' assertions if configured
|
|
193
|
+
if (mergedOptions.allowAsConst && isAsConstAssertion(node.body)) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
context.report({
|
|
197
|
+
node: node.body,
|
|
198
|
+
messageId: 'noTypeAssertionReturns',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
else if (node.body.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
202
|
+
context.report({
|
|
203
|
+
node: node.body,
|
|
204
|
+
messageId: 'noTypeAssertionReturns',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (node.returnType) {
|
|
209
|
+
// For arrow functions with block bodies
|
|
210
|
+
// Allow type predicates if configured
|
|
211
|
+
if (mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
// If type predicates are not allowed, report them
|
|
215
|
+
if (!mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
|
|
216
|
+
context.report({
|
|
217
|
+
node: node.returnType,
|
|
218
|
+
messageId: 'useExplicitVariable',
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// Check if the function has a return statement with a direct value (not a variable)
|
|
223
|
+
if (node.body && node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
224
|
+
for (const statement of node.body.body) {
|
|
225
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
226
|
+
// If returning a variable reference, that's fine
|
|
227
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// If returning an object literal, array literal, or other complex expression
|
|
231
|
+
// without first assigning it to a typed variable, that's a problem
|
|
232
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
233
|
+
statement.argument.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
234
|
+
statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
235
|
+
context.report({
|
|
236
|
+
node: node.returnType,
|
|
237
|
+
messageId: 'useExplicitVariable',
|
|
238
|
+
});
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
// Check for array methods with type assertions
|
|
247
|
+
CallExpression() {
|
|
248
|
+
// Skip this check as we'll handle it in the TSAsExpression and TSTypeAssertion handlers
|
|
249
|
+
},
|
|
250
|
+
// Check for type assertions in expressions
|
|
251
|
+
TSAsExpression(node) {
|
|
252
|
+
// Allow 'as const' assertions if configured
|
|
253
|
+
if (mergedOptions.allowAsConst && isAsConstAssertion(node)) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// If the parent is a return statement, we already handle it in ReturnStatement
|
|
257
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// If the parent is an arrow function, we already handle it in ArrowFunctionExpression
|
|
261
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
// For standalone type assertions in expressions
|
|
265
|
+
context.report({
|
|
266
|
+
node,
|
|
267
|
+
messageId: 'noTypeAssertionReturns',
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
// Check for type assertions using angle bracket syntax
|
|
271
|
+
TSTypeAssertion(node) {
|
|
272
|
+
// If the parent is a return statement, we already handle it in ReturnStatement
|
|
273
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// If the parent is an arrow function, we already handle it in ArrowFunctionExpression
|
|
277
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// For standalone type assertions in expressions
|
|
281
|
+
context.report({
|
|
282
|
+
node,
|
|
283
|
+
messageId: 'noTypeAssertionReturns',
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
//# sourceMappingURL=no-type-assertion-returns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noUnnecessaryVerbSuffix: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unnecessaryVerbSuffix", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noUnnecessaryVerbSuffix = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const COMMON_PREPOSITION_SUFFIXES = new Set([
|
|
7
|
+
// Basic prepositions
|
|
8
|
+
'From',
|
|
9
|
+
'For',
|
|
10
|
+
'With',
|
|
11
|
+
'To',
|
|
12
|
+
'By',
|
|
13
|
+
'In',
|
|
14
|
+
'On',
|
|
15
|
+
'At',
|
|
16
|
+
'Of',
|
|
17
|
+
// Directional prepositions
|
|
18
|
+
'Above',
|
|
19
|
+
'Below',
|
|
20
|
+
'Over',
|
|
21
|
+
'Under',
|
|
22
|
+
'Up',
|
|
23
|
+
'Down',
|
|
24
|
+
'Into',
|
|
25
|
+
'Onto',
|
|
26
|
+
'Out',
|
|
27
|
+
'Off',
|
|
28
|
+
'Through',
|
|
29
|
+
'Throughout',
|
|
30
|
+
'Across',
|
|
31
|
+
'Along',
|
|
32
|
+
'Around',
|
|
33
|
+
'Past',
|
|
34
|
+
// Temporal prepositions
|
|
35
|
+
'After',
|
|
36
|
+
'Before',
|
|
37
|
+
'During',
|
|
38
|
+
'Until',
|
|
39
|
+
'Till',
|
|
40
|
+
'Since',
|
|
41
|
+
'Within',
|
|
42
|
+
// Logical/causal prepositions
|
|
43
|
+
'Because',
|
|
44
|
+
'Despite',
|
|
45
|
+
'Instead',
|
|
46
|
+
'Via',
|
|
47
|
+
'Without',
|
|
48
|
+
'Versus',
|
|
49
|
+
'Vs',
|
|
50
|
+
// Comparative prepositions
|
|
51
|
+
'Than',
|
|
52
|
+
'Like',
|
|
53
|
+
'As',
|
|
54
|
+
// Compound prepositions
|
|
55
|
+
'According',
|
|
56
|
+
'Ahead',
|
|
57
|
+
'Apart',
|
|
58
|
+
'Aside',
|
|
59
|
+
'Away',
|
|
60
|
+
'Back',
|
|
61
|
+
'Forward',
|
|
62
|
+
'Backward',
|
|
63
|
+
'Inside',
|
|
64
|
+
'Outside',
|
|
65
|
+
'Together',
|
|
66
|
+
// Phrasal prepositions (common endings)
|
|
67
|
+
'About',
|
|
68
|
+
'Against',
|
|
69
|
+
'Among',
|
|
70
|
+
'Amongst',
|
|
71
|
+
'Beside',
|
|
72
|
+
'Besides',
|
|
73
|
+
'Between',
|
|
74
|
+
'Beyond',
|
|
75
|
+
'Concerning',
|
|
76
|
+
'Considering',
|
|
77
|
+
'Regarding',
|
|
78
|
+
'Respecting',
|
|
79
|
+
'Towards',
|
|
80
|
+
'Toward',
|
|
81
|
+
'Upon',
|
|
82
|
+
// Preposition-like adverbs
|
|
83
|
+
'Again',
|
|
84
|
+
'Already',
|
|
85
|
+
'Always',
|
|
86
|
+
'Ever',
|
|
87
|
+
'Never',
|
|
88
|
+
'Now',
|
|
89
|
+
'Soon',
|
|
90
|
+
'Then',
|
|
91
|
+
'There',
|
|
92
|
+
'Where',
|
|
93
|
+
'When',
|
|
94
|
+
'While',
|
|
95
|
+
// Programming-specific suffixes
|
|
96
|
+
'Async',
|
|
97
|
+
'Sync',
|
|
98
|
+
]);
|
|
99
|
+
exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
|
|
100
|
+
name: 'no-unnecessary-verb-suffix',
|
|
101
|
+
meta: {
|
|
102
|
+
type: 'suggestion',
|
|
103
|
+
docs: {
|
|
104
|
+
description: 'Prevent unnecessary verb suffixes in function and method names',
|
|
105
|
+
recommended: 'error',
|
|
106
|
+
},
|
|
107
|
+
fixable: 'code',
|
|
108
|
+
schema: [],
|
|
109
|
+
messages: {
|
|
110
|
+
unnecessaryVerbSuffix: 'Unnecessary verb suffix "{{suffix}}" in function name. Consider using "{{suggestion}}" instead.',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
defaultOptions: [],
|
|
114
|
+
create(context) {
|
|
115
|
+
function checkFunctionName(node, name) {
|
|
116
|
+
if (!name)
|
|
117
|
+
return;
|
|
118
|
+
for (const suffix of COMMON_PREPOSITION_SUFFIXES) {
|
|
119
|
+
// Check if the name ends with the suffix
|
|
120
|
+
if (name.endsWith(suffix)) {
|
|
121
|
+
// Make sure there's a verb before the suffix (camelCase format)
|
|
122
|
+
// This regex checks for a verb pattern before the suffix
|
|
123
|
+
// It looks for a word character followed by lowercase letters before the suffix
|
|
124
|
+
const verbBeforeSuffixPattern = new RegExp(`\\w[a-z]+${suffix}$`);
|
|
125
|
+
if (verbBeforeSuffixPattern.test(name)) {
|
|
126
|
+
const suggestion = name.substring(0, name.length - suffix.length);
|
|
127
|
+
// Skip if the suggestion would be empty or just a single character
|
|
128
|
+
if (suggestion.length <= 1)
|
|
129
|
+
continue;
|
|
130
|
+
context.report({
|
|
131
|
+
node,
|
|
132
|
+
messageId: 'unnecessaryVerbSuffix',
|
|
133
|
+
data: {
|
|
134
|
+
suffix,
|
|
135
|
+
suggestion,
|
|
136
|
+
},
|
|
137
|
+
fix(fixer) {
|
|
138
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
139
|
+
return fixer.replaceText(node.id, suggestion);
|
|
140
|
+
}
|
|
141
|
+
else if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
142
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
143
|
+
const parent = node.parent;
|
|
144
|
+
if (parent &&
|
|
145
|
+
(parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator ||
|
|
146
|
+
parent.type === utils_1.AST_NODE_TYPES.Property ||
|
|
147
|
+
parent.type === utils_1.AST_NODE_TYPES.MethodDefinition)) {
|
|
148
|
+
if ('key' in parent) {
|
|
149
|
+
return fixer.replaceText(parent.key, suggestion);
|
|
150
|
+
}
|
|
151
|
+
else if ('id' in parent && parent.id) {
|
|
152
|
+
return fixer.replaceText(parent.id, suggestion);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
FunctionDeclaration(node) {
|
|
165
|
+
if (node.id) {
|
|
166
|
+
checkFunctionName(node, node.id.name);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
VariableDeclarator(node) {
|
|
170
|
+
if (node.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
171
|
+
(node.init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
172
|
+
node.init?.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
173
|
+
checkFunctionName(node.init, node.id.name);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
MethodDefinition(node) {
|
|
177
|
+
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
178
|
+
checkFunctionName(node.value, node.key.name);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
TSMethodSignature(node) {
|
|
182
|
+
// Handle interface method signatures
|
|
183
|
+
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
184
|
+
checkFunctionName(node, node.key.name);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
Property(node) {
|
|
188
|
+
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
189
|
+
(node.value.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
190
|
+
node.value.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
191
|
+
checkFunctionName(node.value, node.key.name);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
FunctionExpression(node) {
|
|
195
|
+
// Handle named function expressions
|
|
196
|
+
if (node.id) {
|
|
197
|
+
checkFunctionName(node, node.id.name);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=no-unnecessary-verb-suffix.js.map
|
|
@@ -36,7 +36,53 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
else if (typeNode.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
39
|
-
typeNode.types.forEach(
|
|
39
|
+
typeNode.types.forEach((type) => {
|
|
40
|
+
if (type.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
41
|
+
const typeName = type.typeName;
|
|
42
|
+
if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
43
|
+
if (typeName.name === 'Pick' && type.typeParameters) {
|
|
44
|
+
// Handle Pick utility type in intersection
|
|
45
|
+
const [baseType, pickedProps] = type.typeParameters.params;
|
|
46
|
+
if (baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
47
|
+
baseType.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
48
|
+
// Extract the picked properties from the union type
|
|
49
|
+
if (pickedProps.type === utils_1.AST_NODE_TYPES.TSUnionType) {
|
|
50
|
+
pickedProps.types.forEach((t) => {
|
|
51
|
+
if (t.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
52
|
+
t.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
53
|
+
typeof t.literal.value === 'string') {
|
|
54
|
+
// Add each picked property as a regular prop
|
|
55
|
+
props[t.literal.value] = t.literal;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (pickedProps.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
60
|
+
pickedProps.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
61
|
+
typeof pickedProps.literal.value === 'string') {
|
|
62
|
+
// Single property pick
|
|
63
|
+
props[pickedProps.literal.value] = pickedProps.literal;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// For referenced types in intersections, we need to find their type declaration
|
|
69
|
+
const scope = context.getScope();
|
|
70
|
+
const variable = scope.variables.find(v => v.name === typeName.name);
|
|
71
|
+
if (variable && variable.defs[0]?.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
72
|
+
extractProps(variable.defs[0].node.typeAnnotation);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// If we can't find the type declaration, it's likely an imported type
|
|
76
|
+
// Mark it as a forwarded prop
|
|
77
|
+
props[`...${typeName.name}`] = typeName;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
extractProps(type);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
40
86
|
}
|
|
41
87
|
else if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
42
88
|
if (typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|