@blumintinc/eslint-plugin-blumint 1.5.0 → 1.5.1
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 +22 -32
- 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
package/lib/index.js
CHANGED
|
@@ -52,10 +52,14 @@ const enforce_memoize_async_1 = require("./rules/enforce-memoize-async");
|
|
|
52
52
|
const enforce_exported_function_types_1 = require("./rules/enforce-exported-function-types");
|
|
53
53
|
const no_redundant_param_types_1 = require("./rules/no-redundant-param-types");
|
|
54
54
|
const no_class_instance_destructuring_1 = require("./rules/no-class-instance-destructuring");
|
|
55
|
+
const no_firestore_object_arrays_1 = require("./rules/no-firestore-object-arrays");
|
|
56
|
+
const no_memoize_on_static_1 = require("./rules/no-memoize-on-static");
|
|
57
|
+
const no_unsafe_firestore_spread_1 = require("./rules/no-unsafe-firestore-spread");
|
|
58
|
+
const no_jsx_in_hooks_1 = require("./rules/no-jsx-in-hooks");
|
|
55
59
|
module.exports = {
|
|
56
60
|
meta: {
|
|
57
61
|
name: '@blumintinc/eslint-plugin-blumint',
|
|
58
|
-
version: '1.5.
|
|
62
|
+
version: '1.5.1',
|
|
59
63
|
},
|
|
60
64
|
parseOptions: {
|
|
61
65
|
ecmaVersion: 2020,
|
|
@@ -113,6 +117,10 @@ module.exports = {
|
|
|
113
117
|
'@blumintinc/blumint/enforce-exported-function-types': 'error',
|
|
114
118
|
'@blumintinc/blumint/no-redundant-param-types': 'error',
|
|
115
119
|
'@blumintinc/blumint/no-class-instance-destructuring': 'error',
|
|
120
|
+
'@blumintinc/blumint/no-firestore-object-arrays': 'warn',
|
|
121
|
+
'@blumintinc/blumint/no-memoize-on-static': 'error',
|
|
122
|
+
'@blumintinc/blumint/no-unsafe-firestore-spread': 'error',
|
|
123
|
+
'@blumintinc/blumint/no-jsx-in-hooks': 'error',
|
|
116
124
|
},
|
|
117
125
|
},
|
|
118
126
|
},
|
|
@@ -166,6 +174,10 @@ module.exports = {
|
|
|
166
174
|
'enforce-exported-function-types': enforce_exported_function_types_1.enforceExportedFunctionTypes,
|
|
167
175
|
'no-redundant-param-types': no_redundant_param_types_1.noRedundantParamTypes,
|
|
168
176
|
'no-class-instance-destructuring': no_class_instance_destructuring_1.noClassInstanceDestructuring,
|
|
177
|
+
'no-firestore-object-arrays': no_firestore_object_arrays_1.noFirestoreObjectArrays,
|
|
178
|
+
'no-memoize-on-static': no_memoize_on_static_1.noMemoizeOnStatic,
|
|
179
|
+
'no-unsafe-firestore-spread': no_unsafe_firestore_spread_1.noUnsafeFirestoreSpread,
|
|
180
|
+
'no-jsx-in-hooks': no_jsx_in_hooks_1.noJsxInHooks,
|
|
169
181
|
},
|
|
170
182
|
};
|
|
171
183
|
//# sourceMappingURL=index.js.map
|
|
@@ -40,17 +40,44 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
|
|
|
40
40
|
}
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
-
function
|
|
43
|
+
function findTypeParameters(node) {
|
|
44
|
+
// Find all type parameters in scope
|
|
45
|
+
const typeParams = new Set();
|
|
46
|
+
let current = node;
|
|
47
|
+
while (current) {
|
|
48
|
+
// Handle type parameters in function declarations
|
|
49
|
+
if (current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
50
|
+
current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
51
|
+
if ('typeParameters' in current && current.typeParameters) {
|
|
52
|
+
current.typeParameters.params.forEach((param) => {
|
|
53
|
+
if (param.type === utils_1.AST_NODE_TYPES.TSTypeParameter) {
|
|
54
|
+
typeParams.add(param.name.name);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
current = current.parent;
|
|
60
|
+
}
|
|
61
|
+
return typeParams;
|
|
62
|
+
}
|
|
63
|
+
function getTypeNames(node, typeParams) {
|
|
44
64
|
if (!node)
|
|
45
65
|
return [];
|
|
66
|
+
// Initialize type parameters if not provided
|
|
67
|
+
if (!typeParams) {
|
|
68
|
+
typeParams = findTypeParameters(node);
|
|
69
|
+
}
|
|
46
70
|
switch (node.type) {
|
|
47
71
|
case utils_1.AST_NODE_TYPES.TSTypeReference:
|
|
48
72
|
if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
73
|
+
// Skip checking generic type parameters (e.g., T in <T extends DocumentData>)
|
|
74
|
+
if (typeParams.has(node.typeName.name))
|
|
75
|
+
return [];
|
|
49
76
|
const names = [node.typeName.name];
|
|
50
|
-
// For generic types like AuthenticatedRequest<Params>, check
|
|
77
|
+
// For generic types like AuthenticatedRequest<Params>, check type arguments
|
|
51
78
|
if ('typeParameters' in node && node.typeParameters) {
|
|
52
79
|
node.typeParameters.params.forEach((param) => {
|
|
53
|
-
names.push(...getTypeNames(param));
|
|
80
|
+
names.push(...getTypeNames(param, typeParams));
|
|
54
81
|
});
|
|
55
82
|
}
|
|
56
83
|
return names;
|
|
@@ -35,8 +35,10 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
MethodDefinition(node) {
|
|
38
|
-
// Only process async methods
|
|
39
|
-
if (node.value.type !== utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
38
|
+
// Only process async instance methods (skip static methods)
|
|
39
|
+
if (node.value.type !== utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
40
|
+
!node.value.async ||
|
|
41
|
+
node.static) {
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
44
|
// Skip methods with more than one parameter
|
|
@@ -7,23 +7,10 @@ exports.enforceVerbNounNaming = void 0;
|
|
|
7
7
|
const utils_1 = require("@typescript-eslint/utils");
|
|
8
8
|
const createRule_1 = require("../utils/createRule");
|
|
9
9
|
const compromise_1 = __importDefault(require("compromise"));
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
'fix',
|
|
15
|
-
'set',
|
|
16
|
-
'log',
|
|
17
|
-
'get',
|
|
18
|
-
'put',
|
|
19
|
-
'add',
|
|
20
|
-
'map',
|
|
21
|
-
'run',
|
|
22
|
-
'use',
|
|
23
|
-
'has',
|
|
24
|
-
'is',
|
|
25
|
-
'do',
|
|
26
|
-
]);
|
|
10
|
+
const verbs_json_1 = __importDefault(require("../utils/verbs.json"));
|
|
11
|
+
const PREPOSITIONS = new Set(['to', 'from', 'with', 'by', 'at', 'of']);
|
|
12
|
+
// Create a Set from the verbs list for O(1) lookup
|
|
13
|
+
const VERBS_SET = new Set(verbs_json_1.default);
|
|
27
14
|
exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
28
15
|
name: 'enforce-verb-noun-naming',
|
|
29
16
|
meta: {
|
|
@@ -47,33 +34,36 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
|
47
34
|
const words = rest.split(/(?=[A-Z])/);
|
|
48
35
|
return firstChar + words[0];
|
|
49
36
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
return 'I ' + name.split(/(?=[A-Z])/).join(' ');
|
|
37
|
+
function toPhrase(name) {
|
|
38
|
+
return name.split(/(?=[A-Z])/).join(' ');
|
|
53
39
|
}
|
|
54
|
-
function getPossibleTags(sentence) {
|
|
40
|
+
function getPossibleTags(sentence, index = 0) {
|
|
55
41
|
const doc = (0, compromise_1.default)(sentence);
|
|
56
42
|
const terms = doc.terms().json();
|
|
57
|
-
if (terms.length
|
|
43
|
+
if (terms.length < index ||
|
|
44
|
+
!terms[index]?.terms?.length ||
|
|
45
|
+
!terms[index]?.terms[0]?.tags)
|
|
58
46
|
return [];
|
|
59
|
-
const tags = terms[
|
|
47
|
+
const tags = terms[index].terms[0].tags;
|
|
60
48
|
return tags;
|
|
61
49
|
}
|
|
62
50
|
function isVerbPhrase(name) {
|
|
63
51
|
const firstWord = extractFirstWord(name);
|
|
64
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
52
|
const firstWordLower = firstWord.toLowerCase();
|
|
66
|
-
// Check for prepositions
|
|
67
|
-
if (PREPOSITIONS.
|
|
53
|
+
// Check for prepositions first
|
|
54
|
+
if (PREPOSITIONS.has(firstWordLower)) {
|
|
68
55
|
return true;
|
|
69
56
|
}
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
// Check against our comprehensive verbs list first
|
|
58
|
+
if (VERBS_SET.has(firstWordLower)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
// Fall back to NLP approach if not found in verbs list
|
|
62
|
+
const withoutPrefixTags = getPossibleTags(toPhrase(name));
|
|
73
63
|
// Check if either form is recognized as a verb
|
|
74
|
-
const isVerb =
|
|
75
|
-
const isPreposition =
|
|
76
|
-
const isConjunction =
|
|
64
|
+
const isVerb = withoutPrefixTags.includes('Verb');
|
|
65
|
+
const isPreposition = withoutPrefixTags.includes('Preposition');
|
|
66
|
+
const isConjunction = withoutPrefixTags.includes('Conjunction');
|
|
77
67
|
// For non-prepositions/conjunctions, require verb form
|
|
78
68
|
if (isPreposition || isConjunction) {
|
|
79
69
|
return true;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noFirestoreObjectArrays: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noObjectArrays", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noFirestoreObjectArrays = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const PRIMITIVE_TYPES = new Set([
|
|
7
|
+
'string',
|
|
8
|
+
'number',
|
|
9
|
+
'boolean',
|
|
10
|
+
'Date',
|
|
11
|
+
'Timestamp',
|
|
12
|
+
'null',
|
|
13
|
+
'undefined',
|
|
14
|
+
'GeoPoint',
|
|
15
|
+
]);
|
|
16
|
+
const isInFirestoreTypesDirectory = (filename) => {
|
|
17
|
+
return filename.includes('functions/src/types/firestore');
|
|
18
|
+
};
|
|
19
|
+
const isObjectType = (node) => {
|
|
20
|
+
switch (node.type) {
|
|
21
|
+
case utils_1.AST_NODE_TYPES.TSTypeLiteral:
|
|
22
|
+
return true;
|
|
23
|
+
case utils_1.AST_NODE_TYPES.TSTypeReference:
|
|
24
|
+
if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
25
|
+
const typeName = node.typeName.name;
|
|
26
|
+
return !PRIMITIVE_TYPES.has(typeName);
|
|
27
|
+
}
|
|
28
|
+
else if (node.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
|
|
29
|
+
// Handle namespace.Type cases
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
case utils_1.AST_NODE_TYPES.TSIntersectionType:
|
|
34
|
+
case utils_1.AST_NODE_TYPES.TSUnionType:
|
|
35
|
+
return node.types.some(isObjectType);
|
|
36
|
+
case utils_1.AST_NODE_TYPES.TSMappedType:
|
|
37
|
+
case utils_1.AST_NODE_TYPES.TSIndexedAccessType:
|
|
38
|
+
return true;
|
|
39
|
+
default:
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
exports.noFirestoreObjectArrays = (0, createRule_1.createRule)({
|
|
44
|
+
name: 'no-firestore-object-arrays',
|
|
45
|
+
meta: {
|
|
46
|
+
type: 'problem',
|
|
47
|
+
docs: {
|
|
48
|
+
description: 'Disallow arrays of objects in Firestore type definitions to optimize performance and avoid unnecessary fetches',
|
|
49
|
+
recommended: 'warn',
|
|
50
|
+
},
|
|
51
|
+
schema: [],
|
|
52
|
+
messages: {
|
|
53
|
+
noObjectArrays: 'Arrays of objects are not recommended in Firestore. Use subcollections, arrays of IDs, or structured maps (Record<string, T>) instead.',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
defaultOptions: [],
|
|
57
|
+
create(context) {
|
|
58
|
+
if (!isInFirestoreTypesDirectory(context.getFilename())) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
TSArrayType(node) {
|
|
63
|
+
if (isObjectType(node.elementType)) {
|
|
64
|
+
context.report({
|
|
65
|
+
node,
|
|
66
|
+
messageId: 'noObjectArrays',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
TSTypeReference(node) {
|
|
71
|
+
// Handle Array<T> and ReadonlyArray<T> syntax
|
|
72
|
+
const typeName = node.typeName.name;
|
|
73
|
+
if ((typeName === 'Array' || typeName === 'ReadonlyArray') && node.typeParameters) {
|
|
74
|
+
const elementType = node.typeParameters.params[0];
|
|
75
|
+
if (isObjectType(elementType)) {
|
|
76
|
+
context.report({
|
|
77
|
+
node,
|
|
78
|
+
messageId: 'noObjectArrays',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=no-firestore-object-arrays.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noJsxInHooks: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noJsxInHooks", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noJsxInHooks = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const isJsxElement = (node) => {
|
|
7
|
+
if (!node)
|
|
8
|
+
return false;
|
|
9
|
+
if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
|
10
|
+
return isJsxElement(node.consequent) || isJsxElement(node.alternate);
|
|
11
|
+
}
|
|
12
|
+
return (node.type === utils_1.AST_NODE_TYPES.JSXElement ||
|
|
13
|
+
node.type === utils_1.AST_NODE_TYPES.JSXFragment ||
|
|
14
|
+
node.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer);
|
|
15
|
+
};
|
|
16
|
+
const isJsxReturnType = (node) => {
|
|
17
|
+
if (node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
18
|
+
const typeName = node.typeAnnotation.typeName;
|
|
19
|
+
if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
20
|
+
return ['JSX', 'ReactNode', 'ReactElement'].includes(typeName.name);
|
|
21
|
+
}
|
|
22
|
+
if (typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
|
|
23
|
+
return typeName.left.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
24
|
+
typeName.left.name === 'JSX' &&
|
|
25
|
+
typeName.right.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
26
|
+
typeName.right.name === 'Element';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
};
|
|
31
|
+
const containsJsxInBlockStatement = (node) => {
|
|
32
|
+
const variablesWithJsx = new Set();
|
|
33
|
+
for (const statement of node.body) {
|
|
34
|
+
// Check variable declarations for JSX assignments
|
|
35
|
+
if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
36
|
+
for (const declarator of statement.declarations) {
|
|
37
|
+
if (declarator.init) {
|
|
38
|
+
if (declarator.init.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
39
|
+
containsJsxInUseMemo(declarator.init)) {
|
|
40
|
+
if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
41
|
+
variablesWithJsx.add(declarator.id.name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Check return statements
|
|
48
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
49
|
+
if (isJsxElement(statement.argument)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
53
|
+
if (containsJsxInUseMemo(statement.argument)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
58
|
+
variablesWithJsx.has(statement.argument.name)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Check if statements
|
|
63
|
+
if (statement.type === utils_1.AST_NODE_TYPES.IfStatement) {
|
|
64
|
+
if (statement.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
65
|
+
statement.consequent.argument) {
|
|
66
|
+
if (isJsxElement(statement.consequent.argument)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (statement.consequent.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
70
|
+
variablesWithJsx.has(statement.consequent.argument.name)) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (statement.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
75
|
+
containsJsxInBlockStatement(statement.consequent)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (statement.alternate) {
|
|
79
|
+
if (statement.alternate.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
80
|
+
statement.alternate.argument) {
|
|
81
|
+
if (isJsxElement(statement.alternate.argument)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (statement.alternate.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
85
|
+
variablesWithJsx.has(statement.alternate.argument.name)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (statement.alternate.type === utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
90
|
+
containsJsxInBlockStatement(statement.alternate)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
};
|
|
98
|
+
const containsJsxInUseMemo = (node) => {
|
|
99
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
100
|
+
node.callee.name === 'useMemo' &&
|
|
101
|
+
node.arguments.length > 0) {
|
|
102
|
+
const callback = node.arguments[0];
|
|
103
|
+
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
104
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
105
|
+
const body = callback.body;
|
|
106
|
+
if (isJsxElement(body)) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
110
|
+
return containsJsxInBlockStatement(body);
|
|
111
|
+
}
|
|
112
|
+
if (body.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
113
|
+
body.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
114
|
+
body.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
115
|
+
body.callee.property.name === 'map') {
|
|
116
|
+
const mapCallback = body.arguments[0];
|
|
117
|
+
if ((mapCallback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
118
|
+
mapCallback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
119
|
+
isJsxElement(mapCallback.body)) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
};
|
|
127
|
+
exports.noJsxInHooks = (0, createRule_1.createRule)({
|
|
128
|
+
name: 'no-jsx-in-hooks',
|
|
129
|
+
meta: {
|
|
130
|
+
type: 'problem',
|
|
131
|
+
docs: {
|
|
132
|
+
description: 'Prevent hooks from returning JSX',
|
|
133
|
+
recommended: 'error',
|
|
134
|
+
},
|
|
135
|
+
schema: [],
|
|
136
|
+
messages: {
|
|
137
|
+
noJsxInHooks: 'Hooks should not return JSX. Convert this hook into a component or extract the JSX into a separate component.',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
defaultOptions: [],
|
|
141
|
+
create(context) {
|
|
142
|
+
return {
|
|
143
|
+
FunctionDeclaration(node) {
|
|
144
|
+
if (node.id && node.id.name.startsWith('use')) {
|
|
145
|
+
// Check return type annotation
|
|
146
|
+
if (node.returnType && isJsxReturnType(node.returnType)) {
|
|
147
|
+
context.report({
|
|
148
|
+
node: node.id,
|
|
149
|
+
messageId: 'noJsxInHooks',
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Check return statements
|
|
154
|
+
if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
155
|
+
if (containsJsxInBlockStatement(node.body)) {
|
|
156
|
+
context.report({
|
|
157
|
+
node: node.id,
|
|
158
|
+
messageId: 'noJsxInHooks',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
ArrowFunctionExpression(node) {
|
|
165
|
+
const parent = node.parent;
|
|
166
|
+
if (parent &&
|
|
167
|
+
parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
168
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
169
|
+
parent.id.name.startsWith('use')) {
|
|
170
|
+
// Check return type annotation
|
|
171
|
+
if (node.returnType && isJsxReturnType(node.returnType)) {
|
|
172
|
+
context.report({
|
|
173
|
+
node: parent.id,
|
|
174
|
+
messageId: 'noJsxInHooks',
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// Check direct JSX return
|
|
179
|
+
if (isJsxElement(node.body)) {
|
|
180
|
+
context.report({
|
|
181
|
+
node: parent.id,
|
|
182
|
+
messageId: 'noJsxInHooks',
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Check block body returns
|
|
187
|
+
if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
188
|
+
if (containsJsxInBlockStatement(node.body)) {
|
|
189
|
+
context.report({
|
|
190
|
+
node: parent.id,
|
|
191
|
+
messageId: 'noJsxInHooks',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
//# sourceMappingURL=no-jsx-in-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noMemoizeOnStatic: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noMemoizeOnStatic", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noMemoizeOnStatic = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.noMemoizeOnStatic = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'no-memoize-on-static',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Prevent using @Memoize() decorator on static methods',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
noMemoizeOnStatic: '@Memoize() decorator should not be used on static methods',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultOptions: [],
|
|
20
|
+
create(context) {
|
|
21
|
+
// Track renamed imports of Memoize
|
|
22
|
+
const memoizeAliases = new Set(['Memoize']);
|
|
23
|
+
return {
|
|
24
|
+
ImportSpecifier(node) {
|
|
25
|
+
if (node.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
26
|
+
node.imported.name === 'Memoize' &&
|
|
27
|
+
node.local.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
28
|
+
memoizeAliases.add(node.local.name);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
'MethodDefinition[static=true]'(node) {
|
|
32
|
+
if (node.decorators) {
|
|
33
|
+
for (const decorator of node.decorators) {
|
|
34
|
+
const expr = decorator.expression;
|
|
35
|
+
if (
|
|
36
|
+
// Handle @Memoize()
|
|
37
|
+
(expr.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
38
|
+
expr.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
39
|
+
memoizeAliases.has(expr.callee.name)) ||
|
|
40
|
+
// Handle @Memoize (without parentheses)
|
|
41
|
+
(expr.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
42
|
+
memoizeAliases.has(expr.name))) {
|
|
43
|
+
context.report({
|
|
44
|
+
node: decorator,
|
|
45
|
+
messageId: 'noMemoizeOnStatic',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=no-memoize-on-static.js.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noUnsafeFirestoreSpread = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.noUnsafeFirestoreSpread = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'no-unsafe-firestore-spread',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Prevent unsafe object/array spreads in Firestore updates',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
unsafeObjectSpread: 'Avoid using object spread in Firestore updates. Use dot notation with FieldPath instead.',
|
|
18
|
+
unsafeArraySpread: 'Avoid using array spread in Firestore updates. Use FieldValue.arrayUnion() instead.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
function isFirestoreSetMergeCall(node) {
|
|
24
|
+
// Check for merge: true in the last argument
|
|
25
|
+
const lastArg = node.arguments[node.arguments.length - 1];
|
|
26
|
+
if (lastArg?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
27
|
+
const hasMergeTrue = lastArg.properties.some((prop) => prop.type === utils_1.AST_NODE_TYPES.Property &&
|
|
28
|
+
prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
29
|
+
prop.key.name === 'merge' &&
|
|
30
|
+
prop.value.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
31
|
+
prop.value.value === true);
|
|
32
|
+
if (!hasMergeTrue)
|
|
33
|
+
return false;
|
|
34
|
+
// Check if it's a set() method call or setDoc() function call
|
|
35
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
36
|
+
const property = node.callee.property;
|
|
37
|
+
return (property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
38
|
+
property.name === 'set');
|
|
39
|
+
}
|
|
40
|
+
else if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
41
|
+
return node.callee.name === 'setDoc';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function checkObjectForSpreads(node, parentPath = '') {
|
|
47
|
+
for (const property of node.properties) {
|
|
48
|
+
if (property.type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
49
|
+
context.report({
|
|
50
|
+
node: property,
|
|
51
|
+
messageId: 'unsafeObjectSpread',
|
|
52
|
+
fix: null,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (property.type === utils_1.AST_NODE_TYPES.Property) {
|
|
56
|
+
const key = property.key.type === utils_1.AST_NODE_TYPES.Identifier
|
|
57
|
+
? property.key.name
|
|
58
|
+
: '';
|
|
59
|
+
const newPath = parentPath ? `${parentPath}.${key}` : key;
|
|
60
|
+
if (property.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
61
|
+
checkObjectForSpreads(property.value, newPath);
|
|
62
|
+
}
|
|
63
|
+
else if (property.value.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
64
|
+
checkArrayForSpreads(property.value);
|
|
65
|
+
}
|
|
66
|
+
else if (property.value.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
67
|
+
// Handle chained array methods like [...array].filter()
|
|
68
|
+
let current = property.value;
|
|
69
|
+
while (current) {
|
|
70
|
+
if (current.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
71
|
+
checkArrayForSpreads(current);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
else if (current.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
75
|
+
checkObjectForSpreads(current, newPath);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
// Move up to check the caller if it's a method chain
|
|
79
|
+
if (current.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
80
|
+
current.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
81
|
+
current = current.callee.object;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function checkArrayForSpreads(node) {
|
|
92
|
+
// Check for spreads in the array expression itself
|
|
93
|
+
for (const element of node.elements) {
|
|
94
|
+
if (element?.type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
95
|
+
context.report({
|
|
96
|
+
node: element,
|
|
97
|
+
messageId: 'unsafeArraySpread',
|
|
98
|
+
fix: null,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
CallExpression(node) {
|
|
105
|
+
if (!isFirestoreSetMergeCall(node))
|
|
106
|
+
return;
|
|
107
|
+
// For set() calls, the update object can be either the first or second argument
|
|
108
|
+
// If it's a docRef.set(data) call, it's the first argument
|
|
109
|
+
// If it's a docRef.set(docRef, data) call (like in transactions/batches), it's the second argument
|
|
110
|
+
let updateArg;
|
|
111
|
+
if (node.arguments.length === 2) {
|
|
112
|
+
updateArg = node.arguments[0];
|
|
113
|
+
}
|
|
114
|
+
else if (node.arguments.length === 3) {
|
|
115
|
+
// In a transaction or batch operation, the data is the second argument
|
|
116
|
+
updateArg = node.arguments[1];
|
|
117
|
+
}
|
|
118
|
+
if (!updateArg || updateArg.type !== utils_1.AST_NODE_TYPES.ObjectExpression)
|
|
119
|
+
return;
|
|
120
|
+
checkObjectForSpreads(updateArg);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=no-unsafe-firestore-spread.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const requireHooksDefaultParams: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"requireDefaultParams", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -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
|