@blumintinc/eslint-plugin-blumint 1.1.9 → 1.2.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 +3 -0
- package/lib/index.js +4 -1
- package/lib/rules/enforce-exported-function-types.d.ts +3 -0
- package/lib/rules/enforce-exported-function-types.js +249 -0
- package/lib/rules/enforce-memoize-async.d.ts +1 -0
- package/lib/rules/enforce-memoize-async.js +70 -0
- package/lib/rules/no-compositing-layer-props.d.ts +1 -0
- package/lib/rules/no-compositing-layer-props.js +127 -0
- package/lib/rules/prefer-settings-object.d.ts +11 -0
- package/lib/rules/prefer-settings-object.js +129 -0
- package/lib/rules/require-image-overlayed.js +2 -0
- package/lib/rules/use-custom-memo.d.ts +1 -0
- package/lib/rules/use-custom-memo.js +65 -0
- package/lib/utils/ASTHelpers.js +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,12 +59,14 @@ Or use the recommended config:
|
|
|
59
59
|
| Name | Description | 💼 | ⚠️ | 🔧 |
|
|
60
60
|
| :--------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :- | :- | :- |
|
|
61
61
|
| [array-methods-this-context](docs/rules/array-methods-this-context.md) | Prevent misuse of Array methods in OOP | | ✅ | |
|
|
62
|
+
| [avoid-utils-directory](docs/rules/avoid-utils-directory.md) | Enforce using util/ instead of utils/ directory | ✅ | | 🔧 |
|
|
62
63
|
| [class-methods-read-top-to-bottom](docs/rules/class-methods-read-top-to-bottom.md) | Ensures classes read linearly from top to bottom. | | ✅ | 🔧 |
|
|
63
64
|
| [consistent-callback-naming](docs/rules/consistent-callback-naming.md) | Enforce consistent naming conventions for callback props and functions | ✅ | | 🔧 |
|
|
64
65
|
| [dynamic-https-errors](docs/rules/dynamic-https-errors.md) | Dynamic error details should only be in the third argument of the HttpsError constructor. The second argument is hashed to produce a unique id. | | ✅ | |
|
|
65
66
|
| [enforce-callable-types](docs/rules/enforce-callable-types.md) | Enforce Params and Response type exports in callable functions | ✅ | | |
|
|
66
67
|
| [enforce-callback-memo](docs/rules/enforce-callback-memo.md) | Enforce useCallback or useMemo for inline functions in JSX props | ✅ | | |
|
|
67
68
|
| [enforce-dynamic-firebase-imports](docs/rules/enforce-dynamic-firebase-imports.md) | Enforce dynamic importing for modules within the firebaseCloud directory | ✅ | | |
|
|
69
|
+
| [enforce-firestore-path-utils](docs/rules/enforce-firestore-path-utils.md) | Enforce usage of utility functions for Firestore paths | ✅ | | |
|
|
68
70
|
| [enforce-identifiable-firestore-type](docs/rules/enforce-identifiable-firestore-type.md) | Enforce that Firestore type definitions extend Identifiable and match their folder name | ✅ | | |
|
|
69
71
|
| [enforce-safe-stringify](docs/rules/enforce-safe-stringify.md) | Enforce using safe-stable-stringify instead of JSON.stringify | ✅ | | 🔧 |
|
|
70
72
|
| [export-if-in-doubt](docs/rules/export-if-in-doubt.md) | All top-level const definitions, type definitions, and functions should be exported | | | |
|
|
@@ -74,6 +76,7 @@ Or use the recommended config:
|
|
|
74
76
|
| [no-async-array-filter](docs/rules/no-async-array-filter.md) | Disallow async callbacks for Array.filter | ✅ | | |
|
|
75
77
|
| [no-async-foreach](docs/rules/no-async-foreach.md) | Disallow Array.forEach with an async callback function | ✅ | | |
|
|
76
78
|
| [no-conditional-literals-in-jsx](docs/rules/no-conditional-literals-in-jsx.md) | Disallow use of conditional literals in JSX code | ✅ | | |
|
|
79
|
+
| [no-entire-object-hook-deps](docs/rules/no-entire-object-hook-deps.md) | Avoid using entire objects in React hook dependency arrays when only specific fields are used | ✅ | | 🔧 |
|
|
77
80
|
| [no-filter-without-return](docs/rules/no-filter-without-return.md) | Disallow Array.filter callbacks without an explicit return (if part of a block statement) | ✅ | | |
|
|
78
81
|
| [no-jsx-whitespace-literal](docs/rules/no-jsx-whitespace-literal.md) | Disallow the use of {" "} elements in JSX code | ✅ | | |
|
|
79
82
|
| [no-misused-switch-case](docs/rules/no-misused-switch-case.md) | Prevent misuse of logical OR in switch case statements | ✅ | | |
|
package/lib/index.js
CHANGED
|
@@ -36,10 +36,11 @@ const enforce_safe_stringify_1 = require("./rules/enforce-safe-stringify");
|
|
|
36
36
|
const avoid_utils_directory_1 = require("./rules/avoid-utils-directory");
|
|
37
37
|
const no_entire_object_hook_deps_1 = require("./rules/no-entire-object-hook-deps");
|
|
38
38
|
const enforce_firestore_path_utils_1 = require("./rules/enforce-firestore-path-utils");
|
|
39
|
+
const no_compositing_layer_props_1 = require("./rules/no-compositing-layer-props");
|
|
39
40
|
module.exports = {
|
|
40
41
|
meta: {
|
|
41
42
|
name: '@blumintinc/eslint-plugin-blumint',
|
|
42
|
-
version: '1.
|
|
43
|
+
version: '1.2.0',
|
|
43
44
|
},
|
|
44
45
|
parseOptions: {
|
|
45
46
|
ecmaVersion: 2020,
|
|
@@ -81,6 +82,7 @@ module.exports = {
|
|
|
81
82
|
'@blumintinc/blumint/require-usememo-object-literals': 'error',
|
|
82
83
|
'@blumintinc/blumint/enforce-safe-stringify': 'error',
|
|
83
84
|
'@blumintinc/blumint/no-entire-object-hook-deps': 'error',
|
|
85
|
+
'@blumintinc/blumint/no-compositing-layer-props': 'warn',
|
|
84
86
|
},
|
|
85
87
|
},
|
|
86
88
|
},
|
|
@@ -118,6 +120,7 @@ module.exports = {
|
|
|
118
120
|
'avoid-utils-directory': avoid_utils_directory_1.avoidUtilsDirectory,
|
|
119
121
|
'no-entire-object-hook-deps': no_entire_object_hook_deps_1.noEntireObjectHookDeps,
|
|
120
122
|
'enforce-firestore-path-utils': enforce_firestore_path_utils_1.enforceFirestorePathUtils,
|
|
123
|
+
'no-compositing-layer-props': no_compositing_layer_props_1.noCompositingLayerProps,
|
|
121
124
|
},
|
|
122
125
|
};
|
|
123
126
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
type MessageIds = 'missingExportedType' | 'missingExportedReturnType' | 'missingExportedPropsType';
|
|
2
|
+
export declare const enforceExportedFunctionTypes: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
+
export {};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceExportedFunctionTypes = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'enforce-exported-function-types',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce exporting types for function props and return values',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
missingExportedType: 'Type {{typeName}} should be exported since it is used in an exported function',
|
|
17
|
+
missingExportedReturnType: 'Return type {{typeName}} should be exported since it is used in an exported function',
|
|
18
|
+
missingExportedPropsType: 'Props type {{typeName}} should be exported since it is used in an exported React component',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
const reportedTypes = new Set();
|
|
24
|
+
function isExported(node) {
|
|
25
|
+
if (!node)
|
|
26
|
+
return false;
|
|
27
|
+
if (node.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
28
|
+
node.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const parent = node.parent;
|
|
32
|
+
if (!parent)
|
|
33
|
+
return false;
|
|
34
|
+
if (parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
35
|
+
parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration ||
|
|
36
|
+
parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator && isExported(parent.parent)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function getTypeName(node) {
|
|
42
|
+
if (!node)
|
|
43
|
+
return undefined;
|
|
44
|
+
switch (node.type) {
|
|
45
|
+
case utils_1.AST_NODE_TYPES.TSTypeReference:
|
|
46
|
+
if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
47
|
+
return node.typeName.name;
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
case utils_1.AST_NODE_TYPES.TSTypeLiteral:
|
|
51
|
+
return 'AnonymousType';
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
function isTypeExported(typeName) {
|
|
56
|
+
const sourceCode = context.getSourceCode();
|
|
57
|
+
const program = sourceCode.ast;
|
|
58
|
+
// Check for exported type declarations
|
|
59
|
+
const exportedTypes = program.body.filter(node => {
|
|
60
|
+
if (node.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
|
|
61
|
+
if (node.declaration?.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
62
|
+
return node.declaration.id.name === typeName;
|
|
63
|
+
}
|
|
64
|
+
if (node.declaration?.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
65
|
+
return node.declaration.id.name === typeName;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
});
|
|
70
|
+
if (exportedTypes.length > 0) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
// Check for type aliases in the current scope
|
|
74
|
+
const scope = context.getScope();
|
|
75
|
+
const variable = scope.variables.find(v => v.name === typeName);
|
|
76
|
+
if (!variable)
|
|
77
|
+
return false;
|
|
78
|
+
const def = variable.defs[0];
|
|
79
|
+
if (!def)
|
|
80
|
+
return false;
|
|
81
|
+
// Check if the type is directly exported
|
|
82
|
+
if (isExported(def.node))
|
|
83
|
+
return true;
|
|
84
|
+
// Check if the type is part of an export declaration
|
|
85
|
+
const parent = def.node.parent;
|
|
86
|
+
if (!parent)
|
|
87
|
+
return false;
|
|
88
|
+
// Handle type aliases in export declarations
|
|
89
|
+
if (parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
// Handle type aliases in variable declarations
|
|
93
|
+
if (parent.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
94
|
+
if (parent.parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Handle type aliases in interface declarations
|
|
99
|
+
if (parent.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
100
|
+
if (parent.parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
FunctionDeclaration(node) {
|
|
108
|
+
if (!isExported(node))
|
|
109
|
+
return;
|
|
110
|
+
// Skip React components
|
|
111
|
+
if (node.id?.name && /^[A-Z]/.test(node.id.name))
|
|
112
|
+
return;
|
|
113
|
+
// Check return type
|
|
114
|
+
if (node.returnType?.typeAnnotation) {
|
|
115
|
+
const typeName = getTypeName(node.returnType.typeAnnotation);
|
|
116
|
+
if (typeName && !isTypeExported(typeName)) {
|
|
117
|
+
// Check if we've already reported this type
|
|
118
|
+
const key = `${typeName}-${node.loc?.start.line}-${node.loc?.start.column}`;
|
|
119
|
+
if (!reportedTypes.has(key)) {
|
|
120
|
+
reportedTypes.add(key);
|
|
121
|
+
context.report({
|
|
122
|
+
node: node.returnType,
|
|
123
|
+
messageId: 'missingExportedReturnType',
|
|
124
|
+
data: { typeName },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Check parameter types
|
|
130
|
+
node.params.forEach(param => {
|
|
131
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
|
|
132
|
+
const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
|
|
133
|
+
if (typeName && !isTypeExported(typeName)) {
|
|
134
|
+
// Check if we've already reported this type
|
|
135
|
+
const key = `${typeName}-${param.loc?.start.line}-${param.loc?.start.column}`;
|
|
136
|
+
if (!reportedTypes.has(key)) {
|
|
137
|
+
reportedTypes.add(key);
|
|
138
|
+
context.report({
|
|
139
|
+
node: param.typeAnnotation,
|
|
140
|
+
messageId: 'missingExportedType',
|
|
141
|
+
data: { typeName },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
'VariableDeclarator > ArrowFunctionExpression'(node) {
|
|
149
|
+
if (!node.parent?.parent || !isExported(node.parent.parent))
|
|
150
|
+
return;
|
|
151
|
+
// Skip React components
|
|
152
|
+
if (node.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
153
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
154
|
+
/^[A-Z]/.test(node.parent.id.name))
|
|
155
|
+
return;
|
|
156
|
+
// Check return type
|
|
157
|
+
if (node.returnType?.typeAnnotation) {
|
|
158
|
+
const typeName = getTypeName(node.returnType.typeAnnotation);
|
|
159
|
+
if (typeName && !isTypeExported(typeName)) {
|
|
160
|
+
// Check if we've already reported this type
|
|
161
|
+
const key = `${typeName}-${node.loc?.start.line}-${node.loc?.start.column}`;
|
|
162
|
+
if (!reportedTypes.has(key)) {
|
|
163
|
+
reportedTypes.add(key);
|
|
164
|
+
context.report({
|
|
165
|
+
node: node.returnType,
|
|
166
|
+
messageId: 'missingExportedReturnType',
|
|
167
|
+
data: { typeName },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Check parameter types
|
|
173
|
+
node.params.forEach(param => {
|
|
174
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
|
|
175
|
+
const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
|
|
176
|
+
if (typeName && !isTypeExported(typeName)) {
|
|
177
|
+
// Check if we've already reported this type
|
|
178
|
+
const key = `${typeName}-${param.loc?.start.line}-${param.loc?.start.column}`;
|
|
179
|
+
if (!reportedTypes.has(key)) {
|
|
180
|
+
reportedTypes.add(key);
|
|
181
|
+
context.report({
|
|
182
|
+
node: param.typeAnnotation,
|
|
183
|
+
messageId: 'missingExportedType',
|
|
184
|
+
data: { typeName },
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
// Handle React components
|
|
192
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation]'(node) {
|
|
193
|
+
if (!isExported(node.parent))
|
|
194
|
+
return;
|
|
195
|
+
// Check props parameter
|
|
196
|
+
if (node.typeAnnotation) {
|
|
197
|
+
const typeName = getTypeName(node.typeAnnotation.typeAnnotation);
|
|
198
|
+
if (typeName && !isTypeExported(typeName)) {
|
|
199
|
+
// Check if we've already reported this type
|
|
200
|
+
const key = `${typeName}-${node.loc?.start.line}-${node.loc?.start.column}`;
|
|
201
|
+
if (!reportedTypes.has(key)) {
|
|
202
|
+
reportedTypes.add(key);
|
|
203
|
+
context.report({
|
|
204
|
+
node: node.typeAnnotation,
|
|
205
|
+
messageId: 'missingExportedType',
|
|
206
|
+
data: { typeName },
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
// Skip type checking for React components since we handle them separately
|
|
214
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > TSTypeAnnotation'() { },
|
|
215
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > TSTypeAnnotation > TSTypeReference'() { },
|
|
216
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > TSTypeAnnotation'() { },
|
|
217
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > TSTypeAnnotation > TSTypeReference'() { },
|
|
218
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation'() { },
|
|
219
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation'() { },
|
|
220
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference'() { },
|
|
221
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference'() { },
|
|
222
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > Identifier'() { },
|
|
223
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > Identifier'() { },
|
|
224
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName'() { },
|
|
225
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName'() { },
|
|
226
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > Identifier'() { },
|
|
227
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > Identifier'() { },
|
|
228
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName'() { },
|
|
229
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName'() { },
|
|
230
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
231
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
232
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
233
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
234
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
235
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
236
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
237
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
238
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
239
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
240
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
241
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
242
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
243
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
|
|
244
|
+
'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
245
|
+
'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
|
|
246
|
+
};
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
//# sourceMappingURL=enforce-exported-function-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const enforceMemoizeAsync: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"requireMemoize", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceMemoizeAsync = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'enforce-memoize-async',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce @Memoize() decorator on async methods with 0-1 parameters',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
requireMemoize: 'Async methods with 0-1 parameters should be decorated with @Memoize()',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
let hasMemoizeImport = false;
|
|
23
|
+
let memoizeAlias = 'Memoize';
|
|
24
|
+
return {
|
|
25
|
+
ImportDeclaration(node) {
|
|
26
|
+
if (node.source.value === 'typescript-memoize') {
|
|
27
|
+
const memoizeSpecifier = node.specifiers.find(spec => spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
28
|
+
spec.imported.name === 'Memoize');
|
|
29
|
+
if (memoizeSpecifier) {
|
|
30
|
+
hasMemoizeImport = true;
|
|
31
|
+
if (memoizeSpecifier.local) {
|
|
32
|
+
memoizeAlias = memoizeSpecifier.local.name;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
MethodDefinition(node) {
|
|
38
|
+
// Only process async methods
|
|
39
|
+
if (node.value.type !== utils_1.AST_NODE_TYPES.FunctionExpression || !node.value.async) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Skip methods with more than one parameter
|
|
43
|
+
if (node.value.params.length > 1) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Check if method already has @Memoize decorator
|
|
47
|
+
const hasDecorator = node.decorators?.some(decorator => {
|
|
48
|
+
if (decorator.expression.type !== utils_1.AST_NODE_TYPES.CallExpression) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const callee = decorator.expression.callee;
|
|
52
|
+
return (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
53
|
+
callee.name === memoizeAlias);
|
|
54
|
+
});
|
|
55
|
+
if (!hasDecorator && hasMemoizeImport) {
|
|
56
|
+
context.report({
|
|
57
|
+
node,
|
|
58
|
+
messageId: 'requireMemoize',
|
|
59
|
+
fix(fixer) {
|
|
60
|
+
// Add import if needed
|
|
61
|
+
// Add decorator
|
|
62
|
+
return fixer.insertTextBefore(node, `@${memoizeAlias}()\n${' '.repeat(node.loc.start.column)}`);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=enforce-memoize-async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noCompositingLayerProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"compositingLayer", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noCompositingLayerProps = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
// Convert camelCase to kebab-case
|
|
7
|
+
function toKebabCase(str) {
|
|
8
|
+
return str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
|
|
9
|
+
}
|
|
10
|
+
// Normalize property name to kebab-case for consistent lookup
|
|
11
|
+
function normalizePropertyName(name) {
|
|
12
|
+
// If already contains hyphens, assume it's kebab-case
|
|
13
|
+
if (name.includes('-'))
|
|
14
|
+
return name.toLowerCase();
|
|
15
|
+
// Convert camelCase to kebab-case
|
|
16
|
+
return toKebabCase(name).toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
const COMPOSITING_PROPERTIES = new Set([
|
|
19
|
+
'filter',
|
|
20
|
+
'backdrop-filter',
|
|
21
|
+
'will-change',
|
|
22
|
+
'transform',
|
|
23
|
+
'perspective',
|
|
24
|
+
'backface-visibility',
|
|
25
|
+
'contain',
|
|
26
|
+
'mix-blend-mode',
|
|
27
|
+
'opacity',
|
|
28
|
+
]);
|
|
29
|
+
const COMPOSITING_VALUES = new Set([
|
|
30
|
+
'translate3d',
|
|
31
|
+
'scale3d',
|
|
32
|
+
'translateZ',
|
|
33
|
+
'transparent',
|
|
34
|
+
]);
|
|
35
|
+
exports.noCompositingLayerProps = (0, createRule_1.createRule)({
|
|
36
|
+
name: 'no-compositing-layer-props',
|
|
37
|
+
meta: {
|
|
38
|
+
type: 'suggestion',
|
|
39
|
+
docs: {
|
|
40
|
+
description: 'Warn when using CSS properties that trigger compositing layers',
|
|
41
|
+
recommended: 'warn',
|
|
42
|
+
},
|
|
43
|
+
schema: [],
|
|
44
|
+
messages: {
|
|
45
|
+
compositingLayer: '{{property}} may trigger a new compositing layer which can impact performance. Consider using alternative properties or add an eslint-disable comment if the layer promotion is intentional.',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
defaultOptions: [],
|
|
49
|
+
create(context) {
|
|
50
|
+
const seenNodes = new WeakSet();
|
|
51
|
+
function checkPropertyValue(value) {
|
|
52
|
+
return COMPOSITING_VALUES.has(value) ||
|
|
53
|
+
value.includes('translate3d') ||
|
|
54
|
+
value.includes('scale3d') ||
|
|
55
|
+
value.includes('translateZ');
|
|
56
|
+
}
|
|
57
|
+
function checkProperty(propertyName, propertyValue) {
|
|
58
|
+
const normalizedName = normalizePropertyName(propertyName);
|
|
59
|
+
if (COMPOSITING_PROPERTIES.has(normalizedName)) {
|
|
60
|
+
// Special case for opacity - only warn if it's animated or fractional
|
|
61
|
+
if (normalizedName === 'opacity') {
|
|
62
|
+
if (!propertyValue)
|
|
63
|
+
return false;
|
|
64
|
+
const numValue = Number.parseFloat(propertyValue);
|
|
65
|
+
if (Number.isNaN(numValue))
|
|
66
|
+
return false;
|
|
67
|
+
return numValue > 0 && numValue < 1;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (propertyValue && checkPropertyValue(propertyValue)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function checkNode(node) {
|
|
77
|
+
// Skip if we've already processed this node
|
|
78
|
+
if (seenNodes.has(node))
|
|
79
|
+
return;
|
|
80
|
+
seenNodes.add(node);
|
|
81
|
+
let propertyName = '';
|
|
82
|
+
let propertyValue = '';
|
|
83
|
+
// Get property name
|
|
84
|
+
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
85
|
+
propertyName = node.key.name;
|
|
86
|
+
}
|
|
87
|
+
else if (node.key.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
88
|
+
propertyName = String(node.key.value);
|
|
89
|
+
}
|
|
90
|
+
// Get property value if it's a string literal or numeric literal
|
|
91
|
+
if (node.value.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
92
|
+
propertyValue = String(node.value.value);
|
|
93
|
+
}
|
|
94
|
+
if (checkProperty(propertyName, propertyValue)) {
|
|
95
|
+
context.report({
|
|
96
|
+
node,
|
|
97
|
+
messageId: 'compositingLayer',
|
|
98
|
+
data: {
|
|
99
|
+
property: propertyName,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
// Handle object literal properties (inline styles)
|
|
106
|
+
Property(node) {
|
|
107
|
+
if (node.parent?.type !== utils_1.AST_NODE_TYPES.ObjectExpression)
|
|
108
|
+
return;
|
|
109
|
+
checkNode(node);
|
|
110
|
+
},
|
|
111
|
+
// Handle JSX style attributes
|
|
112
|
+
JSXAttribute(node) {
|
|
113
|
+
if (node.name.name !== 'style')
|
|
114
|
+
return;
|
|
115
|
+
if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
116
|
+
node.value.expression.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
117
|
+
node.value.expression.properties.forEach((prop) => {
|
|
118
|
+
if (prop.type === utils_1.AST_NODE_TYPES.Property) {
|
|
119
|
+
checkNode(prop);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=no-compositing-layer-props.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type MessageIds = 'tooManyParams' | 'sameTypeParams';
|
|
2
|
+
type Options = [
|
|
3
|
+
{
|
|
4
|
+
minimumParameters?: number;
|
|
5
|
+
checkSameTypeParameters?: boolean;
|
|
6
|
+
ignoreBoundMethods?: boolean;
|
|
7
|
+
ignoreVariadicFunctions?: boolean;
|
|
8
|
+
}
|
|
9
|
+
];
|
|
10
|
+
export declare const preferSettingsObject: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preferSettingsObject = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const defaultOptions = {
|
|
7
|
+
minimumParameters: 3,
|
|
8
|
+
checkSameTypeParameters: true,
|
|
9
|
+
ignoreBoundMethods: true,
|
|
10
|
+
ignoreVariadicFunctions: true,
|
|
11
|
+
};
|
|
12
|
+
exports.preferSettingsObject = (0, createRule_1.createRule)({
|
|
13
|
+
name: 'prefer-settings-object',
|
|
14
|
+
meta: {
|
|
15
|
+
type: 'suggestion',
|
|
16
|
+
docs: {
|
|
17
|
+
description: 'Enforce using a settings object for functions with multiple parameters',
|
|
18
|
+
recommended: 'error',
|
|
19
|
+
},
|
|
20
|
+
fixable: 'code',
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
minimumParameters: {
|
|
26
|
+
type: 'number',
|
|
27
|
+
minimum: 2,
|
|
28
|
+
},
|
|
29
|
+
checkSameTypeParameters: {
|
|
30
|
+
type: 'boolean',
|
|
31
|
+
},
|
|
32
|
+
ignoreBoundMethods: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
},
|
|
35
|
+
ignoreVariadicFunctions: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
additionalProperties: false,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
messages: {
|
|
43
|
+
tooManyParams: 'Function has too many parameters ({{count}}). Use a settings object instead.',
|
|
44
|
+
sameTypeParams: 'Function has multiple parameters of the same type. Use a settings object instead.',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
defaultOptions: [defaultOptions],
|
|
48
|
+
create(context, [options]) {
|
|
49
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
50
|
+
function getParameterType(param) {
|
|
51
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
|
|
52
|
+
const typeNode = param.typeAnnotation.typeAnnotation;
|
|
53
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
54
|
+
return typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier
|
|
55
|
+
? typeNode.typeName.name
|
|
56
|
+
: 'unknown';
|
|
57
|
+
}
|
|
58
|
+
return typeNode.type;
|
|
59
|
+
}
|
|
60
|
+
return 'unknown';
|
|
61
|
+
}
|
|
62
|
+
function hasSameTypeParameters(params) {
|
|
63
|
+
const typeMap = new Map();
|
|
64
|
+
for (const param of params) {
|
|
65
|
+
const type = getParameterType(param);
|
|
66
|
+
typeMap.set(type, (typeMap.get(type) || 0) + 1);
|
|
67
|
+
if (typeMap.get(type) > 1) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function shouldIgnoreNode(node) {
|
|
74
|
+
// Ignore variadic functions if configured
|
|
75
|
+
if (finalOptions.ignoreVariadicFunctions) {
|
|
76
|
+
const hasRestParam = node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
77
|
+
node.params.some(param => param.type === utils_1.AST_NODE_TYPES.RestElement);
|
|
78
|
+
if (hasRestParam)
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
// Ignore bound methods if configured
|
|
82
|
+
if (finalOptions.ignoreBoundMethods) {
|
|
83
|
+
let parent = node.parent;
|
|
84
|
+
while (parent) {
|
|
85
|
+
if (parent.type === utils_1.AST_NODE_TYPES.CallExpression ||
|
|
86
|
+
parent.type === utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
parent = parent.parent;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
function checkFunction(node) {
|
|
95
|
+
if (shouldIgnoreNode(node))
|
|
96
|
+
return;
|
|
97
|
+
const params = node.params;
|
|
98
|
+
// Check for too many parameters
|
|
99
|
+
const minParams = finalOptions.minimumParameters !== undefined
|
|
100
|
+
? finalOptions.minimumParameters
|
|
101
|
+
: defaultOptions.minimumParameters;
|
|
102
|
+
if (params.length >= minParams) {
|
|
103
|
+
context.report({
|
|
104
|
+
node,
|
|
105
|
+
messageId: 'tooManyParams',
|
|
106
|
+
data: { count: params.length },
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Check for same type parameters if enabled
|
|
111
|
+
if (finalOptions.checkSameTypeParameters && params.length >= 2) {
|
|
112
|
+
if (hasSameTypeParameters(params)) {
|
|
113
|
+
context.report({
|
|
114
|
+
node,
|
|
115
|
+
messageId: 'sameTypeParams',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
FunctionDeclaration: checkFunction,
|
|
122
|
+
FunctionExpression: checkFunction,
|
|
123
|
+
ArrowFunctionExpression: checkFunction,
|
|
124
|
+
TSMethodSignature: checkFunction,
|
|
125
|
+
TSFunctionType: checkFunction,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=prefer-settings-object.js.map
|
|
@@ -7,6 +7,7 @@ module.exports = (0, createRule_1.createRule)({
|
|
|
7
7
|
docs: {
|
|
8
8
|
description: 'Enforce using ImageOverlayed component instead of next/image or img tags',
|
|
9
9
|
recommended: 'error',
|
|
10
|
+
requiresTypeChecking: false,
|
|
10
11
|
},
|
|
11
12
|
fixable: 'code',
|
|
12
13
|
schema: [
|
|
@@ -15,6 +16,7 @@ module.exports = (0, createRule_1.createRule)({
|
|
|
15
16
|
properties: {
|
|
16
17
|
componentPath: {
|
|
17
18
|
type: 'string',
|
|
19
|
+
description: 'The import path for the ImageOverlayed component',
|
|
18
20
|
default: 'src/components/ImageOverlayed',
|
|
19
21
|
},
|
|
20
22
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useCustomMemo: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"useCustomMemo", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCustomMemo = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.useCustomMemo = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'use-custom-memo',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce using src/util/memo instead of React memo',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
useCustomMemo: 'Import memo from src/util/memo instead of react',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration(node) {
|
|
24
|
+
if (node.source.value === 'react') {
|
|
25
|
+
const specifiers = node.specifiers.filter((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
26
|
+
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
27
|
+
specifier.imported.name === 'memo');
|
|
28
|
+
if (specifiers.length > 0) {
|
|
29
|
+
context.report({
|
|
30
|
+
node,
|
|
31
|
+
messageId: 'useCustomMemo',
|
|
32
|
+
fix(fixer) {
|
|
33
|
+
// If there are other imports from react, keep them
|
|
34
|
+
const otherSpecifiers = node.specifiers.filter((specifier) => specifier.type !== utils_1.AST_NODE_TYPES.ImportSpecifier ||
|
|
35
|
+
(specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
36
|
+
specifier.imported.name !== 'memo'));
|
|
37
|
+
if (otherSpecifiers.length === 0) {
|
|
38
|
+
// If memo is the only import, replace the entire import
|
|
39
|
+
return fixer.replaceText(node, `import { ${specifiers
|
|
40
|
+
.map((s) => s.local.name !== s.imported.name
|
|
41
|
+
? `memo as ${s.local.name}`
|
|
42
|
+
: 'memo')
|
|
43
|
+
.join(', ')} } from 'src/util/memo';`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Create a new import for memo and keep other imports
|
|
47
|
+
const memoImport = `import { ${specifiers
|
|
48
|
+
.map((s) => s.local.name !== s.imported.name
|
|
49
|
+
? `memo as ${s.local.name}`
|
|
50
|
+
: 'memo')
|
|
51
|
+
.join(', ')} } from 'src/util/memo';\n`;
|
|
52
|
+
const otherImports = `import { ${otherSpecifiers
|
|
53
|
+
.map((s) => s.local.name)
|
|
54
|
+
.join(', ')} } from 'react';`;
|
|
55
|
+
return fixer.replaceText(node, memoImport + otherImports);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=use-custom-memo.js.map
|
package/lib/utils/ASTHelpers.js
CHANGED
|
@@ -91,6 +91,20 @@ class ASTHelpers {
|
|
|
91
91
|
return node.expressions.some((expr) => this.declarationIncludesIdentifier(expr));
|
|
92
92
|
case 'TSAsExpression':
|
|
93
93
|
return this.declarationIncludesIdentifier(node.expression);
|
|
94
|
+
case 'TSTypeReference':
|
|
95
|
+
// Handle type references (e.g., T in generic types)
|
|
96
|
+
return false;
|
|
97
|
+
case 'TSTypeParameterDeclaration':
|
|
98
|
+
// Handle type parameter declarations (e.g., <T extends ...>)
|
|
99
|
+
return false;
|
|
100
|
+
case 'TSTypeParameterInstantiation':
|
|
101
|
+
// Handle type parameter instantiations (e.g., <string>)
|
|
102
|
+
return false;
|
|
103
|
+
case 'TSIntersectionType':
|
|
104
|
+
case 'TSUnionType':
|
|
105
|
+
case 'TSTypeLiteral':
|
|
106
|
+
// Handle type constraints and literals
|
|
107
|
+
return false;
|
|
94
108
|
default:
|
|
95
109
|
return false;
|
|
96
110
|
}
|