@blumintinc/eslint-plugin-blumint 1.5.0 → 1.5.2
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/lib/index.js +13 -1
- package/lib/rules/enforce-exported-function-types.js +30 -3
- package/lib/rules/enforce-memoize-async.js +4 -2
- package/lib/rules/enforce-verb-noun-naming.js +4525 -24
- package/lib/rules/no-firestore-object-arrays.d.ts +1 -0
- package/lib/rules/no-firestore-object-arrays.js +86 -0
- package/lib/rules/no-jsx-in-hooks.d.ts +1 -0
- package/lib/rules/no-jsx-in-hooks.js +200 -0
- package/lib/rules/no-memoize-on-static.d.ts +1 -0
- package/lib/rules/no-memoize-on-static.js +54 -0
- package/lib/rules/no-unsafe-firestore-spread.d.ts +3 -0
- package/lib/rules/no-unsafe-firestore-spread.js +125 -0
- package/lib/rules/require-hooks-default-params.d.ts +1 -0
- package/lib/rules/require-hooks-default-params.js +195 -0
- package/package.json +1 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireHooksDefaultParams = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.requireHooksDefaultParams = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'require-hooks-default-params',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce React hooks with optional parameters to default to an empty object',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
requireDefaultParams: 'React hooks with all optional parameters should default to an empty object',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
function isHookName(name) {
|
|
23
|
+
return name.startsWith('use') && name[3]?.toUpperCase() === name[3];
|
|
24
|
+
}
|
|
25
|
+
function hasAllOptionalProperties(typeNode) {
|
|
26
|
+
// Handle type literals directly
|
|
27
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
28
|
+
return typeNode.members.every(member => {
|
|
29
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return member.optional === true;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Handle type references
|
|
36
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
37
|
+
const typeName = typeNode.typeName;
|
|
38
|
+
if (typeName.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const scope = context.getScope();
|
|
42
|
+
const variable = scope.variables.find(v => v.name === typeName.name);
|
|
43
|
+
if (!variable || !variable.defs[0]?.node) {
|
|
44
|
+
// If we can't find the type definition, assume it's a type with all optional properties
|
|
45
|
+
// This handles cases where the type is imported from another module
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const def = variable.defs[0].node;
|
|
49
|
+
if (def.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
50
|
+
return hasAllOptionalProperties(def.typeAnnotation);
|
|
51
|
+
}
|
|
52
|
+
else if (def.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
53
|
+
return def.body.body.every(member => {
|
|
54
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return member.optional === true;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// If we found the type definition but it's not a type alias or interface declaration,
|
|
61
|
+
// assume it's a type with all optional properties
|
|
62
|
+
// This handles cases where the type is imported from another module
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
// Handle type alias declarations
|
|
66
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
67
|
+
return hasAllOptionalProperties(typeNode.typeAnnotation);
|
|
68
|
+
}
|
|
69
|
+
// Handle interface declarations
|
|
70
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
71
|
+
return typeNode.body.body.every(member => {
|
|
72
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return member.optional === true;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
function checkHookParam(param) {
|
|
81
|
+
// If it's already an assignment pattern, check if the left side is an object pattern
|
|
82
|
+
if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
83
|
+
if (param.left.type === utils_1.AST_NODE_TYPES.ObjectPattern && param.left.typeAnnotation) {
|
|
84
|
+
if (hasAllOptionalProperties(param.left.typeAnnotation.typeAnnotation)) {
|
|
85
|
+
return; // Already has a default value and is correctly typed
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// If it's an object pattern, check if it needs a default value
|
|
91
|
+
if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern && param.typeAnnotation) {
|
|
92
|
+
const typeAnnotation = param.typeAnnotation.typeAnnotation;
|
|
93
|
+
if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
94
|
+
const typeName = typeAnnotation.typeName;
|
|
95
|
+
if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
96
|
+
const scope = context.getScope();
|
|
97
|
+
const variable = scope.variables.find(v => v.name === typeName.name);
|
|
98
|
+
if (!variable || !variable.defs[0]?.node) {
|
|
99
|
+
// If we can't find the type definition, assume it's a type with all optional properties
|
|
100
|
+
// This handles cases where the type is imported from another module
|
|
101
|
+
context.report({
|
|
102
|
+
node: param,
|
|
103
|
+
messageId: 'requireDefaultParams',
|
|
104
|
+
fix(fixer) {
|
|
105
|
+
const paramText = context.getSourceCode().getText(param);
|
|
106
|
+
return fixer.replaceText(param, `${paramText} = {}`);
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const def = variable.defs[0].node;
|
|
112
|
+
if (def.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
113
|
+
if (hasAllOptionalProperties(def.typeAnnotation)) {
|
|
114
|
+
context.report({
|
|
115
|
+
node: param,
|
|
116
|
+
messageId: 'requireDefaultParams',
|
|
117
|
+
fix(fixer) {
|
|
118
|
+
const paramText = context.getSourceCode().getText(param);
|
|
119
|
+
return fixer.replaceText(param, `${paramText} = {}`);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else if (def.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
125
|
+
if (def.body.body.every(member => {
|
|
126
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return member.optional === true;
|
|
130
|
+
})) {
|
|
131
|
+
context.report({
|
|
132
|
+
node: param,
|
|
133
|
+
messageId: 'requireDefaultParams',
|
|
134
|
+
fix(fixer) {
|
|
135
|
+
const paramText = context.getSourceCode().getText(param);
|
|
136
|
+
return fixer.replaceText(param, `${paramText} = {}`);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// If we found the type definition but it's not a type alias or interface declaration,
|
|
143
|
+
// assume it's a type with all optional properties
|
|
144
|
+
// This handles cases where the type is imported from another module
|
|
145
|
+
context.report({
|
|
146
|
+
node: param,
|
|
147
|
+
messageId: 'requireDefaultParams',
|
|
148
|
+
fix(fixer) {
|
|
149
|
+
const paramText = context.getSourceCode().getText(param);
|
|
150
|
+
return fixer.replaceText(param, `${paramText} = {}`);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (hasAllOptionalProperties(typeAnnotation)) {
|
|
157
|
+
context.report({
|
|
158
|
+
node: param,
|
|
159
|
+
messageId: 'requireDefaultParams',
|
|
160
|
+
fix(fixer) {
|
|
161
|
+
const paramText = context.getSourceCode().getText(param);
|
|
162
|
+
return fixer.replaceText(param, `${paramText} = {}`);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
FunctionDeclaration(node) {
|
|
170
|
+
if (!node.id || !isHookName(node.id.name)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (node.params.length !== 1) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
checkHookParam(node.params[0]);
|
|
177
|
+
},
|
|
178
|
+
ArrowFunctionExpression(node) {
|
|
179
|
+
const parent = node.parent;
|
|
180
|
+
if (!parent ||
|
|
181
|
+
parent.type !== utils_1.AST_NODE_TYPES.VariableDeclarator ||
|
|
182
|
+
!parent.id ||
|
|
183
|
+
parent.id.type !== utils_1.AST_NODE_TYPES.Identifier ||
|
|
184
|
+
!isHookName(parent.id.name)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (node.params.length !== 1) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
checkHookParam(node.params[0]);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=require-hooks-default-params.js.map
|