@eslint-react/core 5.2.1-next.1 → 5.2.2-beta.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/dist/index.d.ts +116 -47
- package/dist/index.js +179 -35
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ClassExpression, Directive, FunctionExpression, MethodOrPropertyDefinition } from "@eslint-react/ast";
|
|
2
2
|
import { TSESTree } from "@typescript-eslint/types";
|
|
3
3
|
import { RuleContext } from "@eslint-react/eslint";
|
|
4
4
|
import { RegExpLike } from "@eslint-react/shared";
|
|
@@ -112,6 +112,14 @@ declare const isUseStateCall: isAPICall.ReturnType;
|
|
|
112
112
|
declare const isUseSyncExternalStoreCall: isAPICall.ReturnType;
|
|
113
113
|
declare const isUseTransitionCall: isAPICall.ReturnType;
|
|
114
114
|
//#endregion
|
|
115
|
+
//#region src/class.d.ts
|
|
116
|
+
/**
|
|
117
|
+
* Get the class identifier of a class node
|
|
118
|
+
* @param node The class node to get the identifier from
|
|
119
|
+
* @returns The class identifier or null if not found
|
|
120
|
+
*/
|
|
121
|
+
declare function getClassId(node: ClassExpression): TSESTree.BindingName | null;
|
|
122
|
+
//#endregion
|
|
115
123
|
//#region src/semantic.d.ts
|
|
116
124
|
/**
|
|
117
125
|
* Represents a semantic node in the AST
|
|
@@ -139,9 +147,9 @@ interface SemanticNode {
|
|
|
139
147
|
*/
|
|
140
148
|
interface SemanticFunc extends SemanticNode {
|
|
141
149
|
/** The identifier of the function */
|
|
142
|
-
id:
|
|
150
|
+
id: null | TSESTree.Node;
|
|
143
151
|
/** The AST node of the function */
|
|
144
|
-
node:
|
|
152
|
+
node: FunctionExpression;
|
|
145
153
|
/** The name of the function */
|
|
146
154
|
name: string | null;
|
|
147
155
|
/** The return type annotation of the function */
|
|
@@ -149,7 +157,7 @@ interface SemanticFunc extends SemanticNode {
|
|
|
149
157
|
/** The body of the function */
|
|
150
158
|
body: TSESTree.BlockStatement | TSESTree.Expression;
|
|
151
159
|
/** The directives of the function (ex: "use strict", "use client", "use server", etc.) */
|
|
152
|
-
directives:
|
|
160
|
+
directives: Directive[];
|
|
153
161
|
/** The parameters of the function */
|
|
154
162
|
parameters: TSESTree.Parameter[];
|
|
155
163
|
/** The type parameters of the function */
|
|
@@ -166,68 +174,68 @@ interface ClassComponentSemanticNode extends SemanticNode {
|
|
|
166
174
|
displayName: null | TSESTree.Expression;
|
|
167
175
|
flag: bigint;
|
|
168
176
|
hint: bigint;
|
|
169
|
-
methods:
|
|
170
|
-
node:
|
|
177
|
+
methods: MethodOrPropertyDefinition[];
|
|
178
|
+
node: ClassExpression;
|
|
171
179
|
}
|
|
172
180
|
/**
|
|
173
181
|
* @param node The AST node to check.
|
|
174
182
|
* @deprecated Class components are legacy. This function exists only to support legacy rules.
|
|
175
183
|
*/
|
|
176
|
-
declare function isClassComponent(node: TSESTree.Node): node is
|
|
184
|
+
declare function isClassComponent(node: TSESTree.Node): node is ClassExpression;
|
|
177
185
|
/**
|
|
178
186
|
* @param node The AST node to check.
|
|
179
187
|
* @param context The rule context.
|
|
180
188
|
* @deprecated Class components are legacy. This function exists only to support legacy rules.
|
|
181
189
|
*/
|
|
182
|
-
declare function isClassComponent(node: TSESTree.Node, context: RuleContext): node is
|
|
183
|
-
declare function isClassComponentLoose(node: TSESTree.Node): node is
|
|
190
|
+
declare function isClassComponent(node: TSESTree.Node, context: RuleContext): node is ClassExpression;
|
|
191
|
+
declare function isClassComponentLoose(node: TSESTree.Node): node is ClassExpression;
|
|
184
192
|
/**
|
|
185
193
|
* @param node The AST node to check.
|
|
186
194
|
* @deprecated Class components are legacy. This function exists only to support legacy rules.
|
|
187
195
|
*/
|
|
188
196
|
declare function isPureComponent(node: TSESTree.Node): boolean;
|
|
189
197
|
/** @deprecated Class components are legacy. */
|
|
190
|
-
declare const isRender: (node: TSESTree.Node) => node is
|
|
198
|
+
declare const isRender: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
191
199
|
/** @deprecated Class components are legacy. */
|
|
192
|
-
declare const isComponentDidCatch: (node: TSESTree.Node) => node is
|
|
200
|
+
declare const isComponentDidCatch: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
193
201
|
/** @deprecated Class components are legacy. */
|
|
194
|
-
declare const isComponentDidMount: (node: TSESTree.Node) => node is
|
|
202
|
+
declare const isComponentDidMount: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
195
203
|
/** @deprecated Class components are legacy. */
|
|
196
|
-
declare const isComponentDidUpdate: (node: TSESTree.Node) => node is
|
|
204
|
+
declare const isComponentDidUpdate: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
197
205
|
/** @deprecated Class components are legacy. */
|
|
198
|
-
declare const isComponentWillMount: (node: TSESTree.Node) => node is
|
|
206
|
+
declare const isComponentWillMount: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
199
207
|
/** @deprecated Class components are legacy. */
|
|
200
|
-
declare const isComponentWillReceiveProps: (node: TSESTree.Node) => node is
|
|
208
|
+
declare const isComponentWillReceiveProps: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
201
209
|
/** @deprecated Class components are legacy. */
|
|
202
|
-
declare const isComponentWillUnmount: (node: TSESTree.Node) => node is
|
|
210
|
+
declare const isComponentWillUnmount: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
203
211
|
/** @deprecated Class components are legacy. */
|
|
204
|
-
declare const isComponentWillUpdate: (node: TSESTree.Node) => node is
|
|
212
|
+
declare const isComponentWillUpdate: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
205
213
|
/** @deprecated Class components are legacy. */
|
|
206
|
-
declare const isGetChildContext: (node: TSESTree.Node) => node is
|
|
214
|
+
declare const isGetChildContext: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
207
215
|
/** @deprecated Class components are legacy. */
|
|
208
|
-
declare const isGetInitialState: (node: TSESTree.Node) => node is
|
|
216
|
+
declare const isGetInitialState: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
209
217
|
/** @deprecated Class components are legacy. */
|
|
210
|
-
declare const isGetSnapshotBeforeUpdate: (node: TSESTree.Node) => node is
|
|
218
|
+
declare const isGetSnapshotBeforeUpdate: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
211
219
|
/** @deprecated Class components are legacy. */
|
|
212
|
-
declare const isShouldComponentUpdate: (node: TSESTree.Node) => node is
|
|
220
|
+
declare const isShouldComponentUpdate: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
213
221
|
/** @deprecated Class components are legacy. */
|
|
214
|
-
declare const isUnsafeComponentWillMount: (node: TSESTree.Node) => node is
|
|
222
|
+
declare const isUnsafeComponentWillMount: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
215
223
|
/** @deprecated Class components are legacy. */
|
|
216
|
-
declare const isUnsafeComponentWillReceiveProps: (node: TSESTree.Node) => node is
|
|
224
|
+
declare const isUnsafeComponentWillReceiveProps: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
217
225
|
/** @deprecated Class components are legacy. */
|
|
218
|
-
declare const isUnsafeComponentWillUpdate: (node: TSESTree.Node) => node is
|
|
226
|
+
declare const isUnsafeComponentWillUpdate: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
219
227
|
/** @deprecated Class components are legacy. */
|
|
220
|
-
declare const isGetDefaultProps: (node: TSESTree.Node) => node is
|
|
228
|
+
declare const isGetDefaultProps: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
221
229
|
/** @deprecated Class components are legacy. */
|
|
222
|
-
declare const isGetDerivedStateFromProps: (node: TSESTree.Node) => node is
|
|
230
|
+
declare const isGetDerivedStateFromProps: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
223
231
|
/** @deprecated Class components are legacy. */
|
|
224
|
-
declare const isGetDerivedStateFromError: (node: TSESTree.Node) => node is
|
|
232
|
+
declare const isGetDerivedStateFromError: (node: TSESTree.Node) => node is MethodOrPropertyDefinition;
|
|
225
233
|
/**
|
|
226
234
|
* @param node The AST node to check.
|
|
227
235
|
* @deprecated Class components are legacy. This function exists only to support legacy rules.
|
|
228
236
|
*/
|
|
229
|
-
declare function isRenderMethodLike(node: TSESTree.Node): node is
|
|
230
|
-
declare function isRenderMethodCallback(node:
|
|
237
|
+
declare function isRenderMethodLike(node: TSESTree.Node): node is MethodOrPropertyDefinition;
|
|
238
|
+
declare function isRenderMethodCallback(node: FunctionExpression): boolean;
|
|
231
239
|
/**
|
|
232
240
|
* @param node The call expression node to check.
|
|
233
241
|
* @deprecated Class components are legacy. This function exists only to support legacy rules.
|
|
@@ -259,31 +267,92 @@ declare function getClassComponentCollector(context: RuleContext): getClassCompo
|
|
|
259
267
|
//#endregion
|
|
260
268
|
//#region src/function.d.ts
|
|
261
269
|
/**
|
|
262
|
-
*
|
|
270
|
+
* Type representing the return type of `getFunctionId`.
|
|
271
|
+
*/
|
|
272
|
+
type FunctionID = ReturnType<typeof getFunctionId>;
|
|
273
|
+
/**
|
|
274
|
+
* Represents various AST paths for function declarations.
|
|
275
|
+
* Each tuple type represents a specific function definition pattern.
|
|
276
|
+
*/
|
|
277
|
+
type FunctionInitPath = readonly [TSESTree.FunctionDeclaration] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, FunctionExpression] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.CallExpression, FunctionExpression] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.CallExpression, TSESTree.CallExpression, FunctionExpression] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.ObjectExpression, TSESTree.Property, FunctionExpression] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.ObjectExpression, TSESTree.Property, TSESTree.CallExpression, FunctionExpression] | readonly [TSESTree.VariableDeclaration, TSESTree.VariableDeclarator, TSESTree.ObjectExpression, TSESTree.Property, TSESTree.CallExpression, TSESTree.CallExpression, FunctionExpression] | readonly [TSESTree.ClassDeclaration | TSESTree.ClassExpression, TSESTree.ClassBody, TSESTree.MethodDefinition, FunctionExpression] | readonly [TSESTree.ClassDeclaration | TSESTree.ClassExpression, TSESTree.ClassBody, TSESTree.PropertyDefinition, FunctionExpression];
|
|
278
|
+
/**
|
|
279
|
+
* Represents the kind of a function.
|
|
263
280
|
*/
|
|
264
281
|
type FunctionKind = "client-function" | "server-function";
|
|
265
282
|
/**
|
|
266
|
-
* Represents a
|
|
283
|
+
* Represents a client function semantic node.
|
|
267
284
|
*/
|
|
268
285
|
interface ClientFunctionSemanticNode extends SemanticFunc {
|
|
269
286
|
/**
|
|
270
|
-
* The kind of function
|
|
287
|
+
* The kind of function.
|
|
271
288
|
*/
|
|
272
289
|
kind: "client-function";
|
|
273
290
|
}
|
|
274
291
|
/**
|
|
275
|
-
* Represents a
|
|
292
|
+
* Represents a server function semantic node.
|
|
276
293
|
*/
|
|
277
294
|
interface ServerFunctionSemanticNode extends SemanticFunc {
|
|
278
295
|
/**
|
|
279
|
-
* The kind of function
|
|
296
|
+
* The kind of function.
|
|
280
297
|
*/
|
|
281
298
|
kind: "server-function";
|
|
282
299
|
}
|
|
283
300
|
/**
|
|
284
|
-
* Represents a
|
|
301
|
+
* Represents a function semantic node.
|
|
285
302
|
*/
|
|
286
303
|
type FunctionSemanticNode = ClientFunctionSemanticNode | ServerFunctionSemanticNode;
|
|
304
|
+
/**
|
|
305
|
+
* Gets the static identifier of a function AST node.
|
|
306
|
+
*
|
|
307
|
+
* @remarks
|
|
308
|
+
* For function declarations this is straightforward. For anonymous function
|
|
309
|
+
* expressions it is more complex. This function roughly detects the same AST
|
|
310
|
+
* nodes as the ECMAScript spec's `IsAnonymousFunctionDefinition()` with some
|
|
311
|
+
* exceptions to better fit our use case.
|
|
312
|
+
*
|
|
313
|
+
* Ported from {@link https://github.com/facebook/react/blob/bb8a76c6cc77ea2976d690ea09f5a1b3d9b1792a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L860 | RulesOfHooks.ts}
|
|
314
|
+
*
|
|
315
|
+
* @param node - The function node to analyze.
|
|
316
|
+
* @returns The identifier node if found, `null` otherwise.
|
|
317
|
+
*/
|
|
318
|
+
declare function getFunctionId(node: TSESTree.Expression | FunctionExpression): TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AwaitExpression | TSESTree.PrivateInExpression | TSESTree.SymmetricBinaryExpression | TSESTree.CallExpression | TSESTree.ChainExpression | TSESTree.ClassExpression | TSESTree.ConditionalExpression | TSESTree.FunctionExpression | TSESTree.Identifier | TSESTree.ImportExpression | TSESTree.JSXElement | TSESTree.JSXFragment | TSESTree.BigIntLiteral | TSESTree.BooleanLiteral | TSESTree.NullLiteral | TSESTree.NumberLiteral | TSESTree.RegExpLiteral | TSESTree.StringLiteral | TSESTree.TemplateLiteral | TSESTree.LogicalExpression | TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedName | TSESTree.MetaProperty | TSESTree.NewExpression | TSESTree.ObjectExpression | TSESTree.ObjectPattern | TSESTree.SequenceExpression | TSESTree.Super | TSESTree.TaggedTemplateExpression | TSESTree.ThisExpression | TSESTree.TSAsExpression | TSESTree.TSInstantiationExpression | TSESTree.TSNonNullExpression | TSESTree.TSSatisfiesExpression | TSESTree.TSTypeAssertion | TSESTree.UnaryExpressionBitwiseNot | TSESTree.UnaryExpressionDelete | TSESTree.UnaryExpressionMinus | TSESTree.UnaryExpressionNot | TSESTree.UnaryExpressionPlus | TSESTree.UnaryExpressionTypeof | TSESTree.UnaryExpressionVoid | TSESTree.UpdateExpression | TSESTree.YieldExpression | TSESTree.PrivateIdentifier | null;
|
|
319
|
+
/**
|
|
320
|
+
* Identifies the initialization path of a function node in the AST.
|
|
321
|
+
*
|
|
322
|
+
* @param node - The function node to analyze.
|
|
323
|
+
* @returns The function initialization path or `null` if not identifiable.
|
|
324
|
+
*/
|
|
325
|
+
declare function getFunctionInitPath(node: FunctionExpression): null | FunctionInitPath;
|
|
326
|
+
/**
|
|
327
|
+
* Checks if a specific function call exists in the function initialization path.
|
|
328
|
+
*
|
|
329
|
+
* @param callName - The name of the call to check for (e.g., "memo", "forwardRef").
|
|
330
|
+
* @param initPath - The function initialization path to search in.
|
|
331
|
+
* @returns `true` if the call exists in the path, `false` otherwise.
|
|
332
|
+
*/
|
|
333
|
+
declare function isFunctionHasCallInInitPath(callName: string, initPath: FunctionInitPath): boolean;
|
|
334
|
+
/**
|
|
335
|
+
* Checks if a function is empty.
|
|
336
|
+
*
|
|
337
|
+
* @param node - The function node to check.
|
|
338
|
+
* @returns `true` if the function is empty, `false` otherwise.
|
|
339
|
+
*/
|
|
340
|
+
declare function isFunctionEmpty(node: FunctionExpression): boolean;
|
|
341
|
+
/**
|
|
342
|
+
* Gets all directive expression statements from the top of a function body.
|
|
343
|
+
*
|
|
344
|
+
* @param node - The function AST node.
|
|
345
|
+
* @returns An array of directive expression statements.
|
|
346
|
+
*/
|
|
347
|
+
declare function getFunctionDirectives(node: FunctionExpression): Directive[];
|
|
348
|
+
/**
|
|
349
|
+
* Checks if a directive with the given name exists in the function directives.
|
|
350
|
+
*
|
|
351
|
+
* @param node - The function AST node.
|
|
352
|
+
* @param name - The directive name to check (e.g., "use memo", "use no memo").
|
|
353
|
+
* @returns `true` if the directive exists, `false` otherwise.
|
|
354
|
+
*/
|
|
355
|
+
declare function isFunctionHasDirective(node: FunctionExpression, name: string): boolean;
|
|
287
356
|
//#endregion
|
|
288
357
|
//#region src/function-component.d.ts
|
|
289
358
|
/**
|
|
@@ -293,7 +362,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
|
|
|
293
362
|
/**
|
|
294
363
|
* The identifier or identifier sequence of the component
|
|
295
364
|
*/
|
|
296
|
-
id:
|
|
365
|
+
id: FunctionID;
|
|
297
366
|
/**
|
|
298
367
|
* The kind of component
|
|
299
368
|
*/
|
|
@@ -301,7 +370,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
|
|
|
301
370
|
/**
|
|
302
371
|
* The AST node of the function
|
|
303
372
|
*/
|
|
304
|
-
node:
|
|
373
|
+
node: FunctionExpression;
|
|
305
374
|
/**
|
|
306
375
|
* Flags describing the component's characteristics
|
|
307
376
|
*/
|
|
@@ -317,7 +386,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
|
|
|
317
386
|
/**
|
|
318
387
|
* The initialization path of the function
|
|
319
388
|
*/
|
|
320
|
-
initPath: null |
|
|
389
|
+
initPath: null | FunctionInitPath;
|
|
321
390
|
/**
|
|
322
391
|
* Indicates if the component is inside an export default declaration
|
|
323
392
|
*/
|
|
@@ -337,7 +406,7 @@ interface FunctionComponentSemanticNode extends SemanticNode {
|
|
|
337
406
|
/**
|
|
338
407
|
* The directives used in the function (ex: "use strict", "use client", etc.)
|
|
339
408
|
*/
|
|
340
|
-
directives:
|
|
409
|
+
directives: Directive[];
|
|
341
410
|
}
|
|
342
411
|
/**
|
|
343
412
|
* Component flag constants
|
|
@@ -376,7 +445,7 @@ declare function isFunctionComponentWrapperCallback(context: RuleContext, node:
|
|
|
376
445
|
* @param node The AST node to get the function component identifier from
|
|
377
446
|
* @internal
|
|
378
447
|
*/
|
|
379
|
-
declare function getFunctionComponentId(context: RuleContext, node:
|
|
448
|
+
declare function getFunctionComponentId(context: RuleContext, node: FunctionExpression): FunctionID;
|
|
380
449
|
/**
|
|
381
450
|
* Check if a string matches the strict component name pattern
|
|
382
451
|
* @param name The name to check
|
|
@@ -394,7 +463,7 @@ declare function isFunctionComponentNameLoose(name: string): boolean;
|
|
|
394
463
|
* @param allowNone Whether to allow no name
|
|
395
464
|
* @returns Whether the function has a loose component name
|
|
396
465
|
*/
|
|
397
|
-
declare function isFunctionWithLooseComponentName(context: RuleContext, fn:
|
|
466
|
+
declare function isFunctionWithLooseComponentName(context: RuleContext, fn: FunctionExpression, allowNone?: boolean): boolean;
|
|
398
467
|
type FunctionComponentDetectionHint = bigint;
|
|
399
468
|
/**
|
|
400
469
|
* Hints for component collector
|
|
@@ -433,7 +502,7 @@ declare const DEFAULT_COMPONENT_DETECTION_HINT: bigint;
|
|
|
433
502
|
* @param hint Component detection hints (bit flags) to customize detection logic
|
|
434
503
|
* @returns `true` if the node is considered a component definition
|
|
435
504
|
*/
|
|
436
|
-
declare function isFunctionComponentDefinition(context: RuleContext, node:
|
|
505
|
+
declare function isFunctionComponentDefinition(context: RuleContext, node: FunctionExpression, hint: bigint): boolean;
|
|
437
506
|
//#endregion
|
|
438
507
|
//#region src/function-component-collector.d.ts
|
|
439
508
|
declare namespace getFunctionComponentCollector {
|
|
@@ -462,9 +531,9 @@ declare function getFunctionComponentCollector(context: RuleContext, options?: g
|
|
|
462
531
|
*/
|
|
463
532
|
interface HookSemanticNode extends SemanticNode {
|
|
464
533
|
/** The identifier of the hook */
|
|
465
|
-
id:
|
|
534
|
+
id: FunctionID;
|
|
466
535
|
/** The AST node of the hook */
|
|
467
|
-
node:
|
|
536
|
+
node: FunctionExpression;
|
|
468
537
|
/** The kind of hook */
|
|
469
538
|
kind: "hook";
|
|
470
539
|
/** List of expressions returned by the hook */
|
|
@@ -472,7 +541,7 @@ interface HookSemanticNode extends SemanticNode {
|
|
|
472
541
|
/** The other hooks called by the hook */
|
|
473
542
|
hookCalls: TSESTree.CallExpression[];
|
|
474
543
|
/** The directives used in the function (ex: "use strict", "use client", etc.) */
|
|
475
|
-
directives:
|
|
544
|
+
directives: Directive[];
|
|
476
545
|
}
|
|
477
546
|
declare const REACT_BUILTIN_HOOK_NAMES: readonly ["use", "useActionState", "useCallback", "useContext", "useDebugValue", "useDeferredValue", "useEffect", "useFormStatus", "useId", "useImperativeHandle", "useInsertionEffect", "useLayoutEffect", "useMemo", "useOptimistic", "useReducer", "useRef", "useState", "useSyncExternalStore", "useTransition"];
|
|
478
547
|
/**
|
|
@@ -493,7 +562,7 @@ declare function isHookId(id: TSESTree.Node): id is TSESTree.Identifier | TSESTr
|
|
|
493
562
|
* @param node The function node to check
|
|
494
563
|
* @returns True if the function is a React Hook, false otherwise
|
|
495
564
|
*/
|
|
496
|
-
declare function isHookDefinition(node:
|
|
565
|
+
declare function isHookDefinition(node: FunctionExpression | null): boolean;
|
|
497
566
|
/**
|
|
498
567
|
* Check if the given node is a React Hook call by its name.
|
|
499
568
|
* @param node The node to check.
|
|
@@ -669,4 +738,4 @@ type TypeVariant = "any" | "bigint" | "boolean" | "enum" | "never" | "nullish" |
|
|
|
669
738
|
*/
|
|
670
739
|
declare function getTypeVariants(types: ts.Type[]): Set<TypeVariant>;
|
|
671
740
|
//#endregion
|
|
672
|
-
export { ClassComponentSemanticNode, ClientFunctionSemanticNode, DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, FunctionComponentSemanticNode, FunctionKind, FunctionSemanticNode, HookSemanticNode, REACT_BUILTIN_HOOK_NAMES, SemanticFunc, SemanticNode, ServerFunctionSemanticNode, TypeVariant, getClassComponentCollector, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFunctionComponentDefinition, isFunctionComponentName, isFunctionComponentNameLoose, isFunctionComponentWrapperCall, isFunctionComponentWrapperCallback, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isStringType, isThisSetStateCall, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUse, isUseActionState, isUseActionStateCall, isUseCall, isUseCallback, isUseCallbackCall, isUseContext, isUseContextCall, isUseDebugValue, isUseDebugValueCall, isUseDeferredValue, isUseDeferredValueCall, isUseEffect, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatus, isUseFormStatusCall, isUseId, isUseIdCall, isUseImperativeHandle, isUseImperativeHandleCall, isUseInsertionEffect, isUseInsertionEffectCall, isUseLayoutEffect, isUseLayoutEffectCall, isUseMemo, isUseMemoCall, isUseOptimistic, isUseOptimisticCall, isUseReducer, isUseReducerCall, isUseRef, isUseRefCall, isUseState, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStore, isUseSyncExternalStoreCall, isUseTransition, isUseTransitionCall };
|
|
741
|
+
export { ClassComponentSemanticNode, ClientFunctionSemanticNode, DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, FunctionComponentSemanticNode, FunctionID, FunctionInitPath, FunctionKind, FunctionSemanticNode, HookSemanticNode, REACT_BUILTIN_HOOK_NAMES, SemanticFunc, SemanticNode, ServerFunctionSemanticNode, TypeVariant, getClassComponentCollector, getClassId, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getFunctionDirectives, getFunctionId, getFunctionInitPath, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFunctionComponentDefinition, isFunctionComponentName, isFunctionComponentNameLoose, isFunctionComponentWrapperCall, isFunctionComponentWrapperCallback, isFunctionEmpty, isFunctionHasCallInInitPath, isFunctionHasDirective, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isStringType, isThisSetStateCall, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUse, isUseActionState, isUseActionStateCall, isUseCall, isUseCallback, isUseCallbackCall, isUseContext, isUseContextCall, isUseDebugValue, isUseDebugValueCall, isUseDeferredValue, isUseDeferredValueCall, isUseEffect, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatus, isUseFormStatusCall, isUseId, isUseIdCall, isUseImperativeHandle, isUseImperativeHandleCall, isUseInsertionEffect, isUseInsertionEffectCall, isUseLayoutEffect, isUseLayoutEffectCall, isUseMemo, isUseMemoCall, isUseOptimistic, isUseOptimisticCall, isUseReducer, isUseReducerCall, isUseRef, isUseRefCall, isUseState, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStore, isUseSyncExternalStoreCall, isUseTransition, isUseTransitionCall };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Check, Extract, Select, Traverse, is, isOneOf } from "@eslint-react/ast";
|
|
2
2
|
import { resolveImportSource } from "@eslint-react/var";
|
|
3
3
|
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
4
4
|
import "@eslint-react/eslint";
|
|
@@ -145,7 +145,7 @@ function isAPI(api) {
|
|
|
145
145
|
const func = (context, node) => {
|
|
146
146
|
if (node == null) return false;
|
|
147
147
|
const getText = (n) => context.sourceCode.getText(n);
|
|
148
|
-
const name =
|
|
148
|
+
const name = Extract.fullyQualifiedName(node, getText);
|
|
149
149
|
if (name === api) return true;
|
|
150
150
|
if (name.endsWith(`.${api}`)) return true;
|
|
151
151
|
return false;
|
|
@@ -161,7 +161,7 @@ function isAPICall(api) {
|
|
|
161
161
|
const func = (context, node) => {
|
|
162
162
|
if (node == null) return false;
|
|
163
163
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
164
|
-
return isAPI(api)(context,
|
|
164
|
+
return isAPI(api)(context, Extract.unwrapped(node.callee));
|
|
165
165
|
};
|
|
166
166
|
return dual(2, func);
|
|
167
167
|
}
|
|
@@ -254,6 +254,19 @@ const isUseStateCall = isAPICall("useState");
|
|
|
254
254
|
const isUseSyncExternalStoreCall = isAPICall("useSyncExternalStore");
|
|
255
255
|
const isUseTransitionCall = isAPICall("useTransition");
|
|
256
256
|
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/class.ts
|
|
259
|
+
/**
|
|
260
|
+
* Get the class identifier of a class node
|
|
261
|
+
* @param node The class node to get the identifier from
|
|
262
|
+
* @returns The class identifier or null if not found
|
|
263
|
+
*/
|
|
264
|
+
function getClassId(node) {
|
|
265
|
+
if (node.id != null) return node.id;
|
|
266
|
+
if (node.parent.type === AST_NODE_TYPES.VariableDeclarator) return node.parent.id;
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
257
270
|
//#endregion
|
|
258
271
|
//#region src/class-component.ts
|
|
259
272
|
function isClassComponent(node, context) {
|
|
@@ -298,7 +311,7 @@ function isPureComponent(node) {
|
|
|
298
311
|
return false;
|
|
299
312
|
}
|
|
300
313
|
function createLifecycleChecker(methodName, isStatic = false) {
|
|
301
|
-
return (node) =>
|
|
314
|
+
return (node) => Check.isMethodOrProperty(node) && node.static === isStatic && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === methodName;
|
|
302
315
|
}
|
|
303
316
|
/** @deprecated Class components are legacy. */
|
|
304
317
|
const isRender = createLifecycleChecker("render");
|
|
@@ -341,7 +354,7 @@ const isGetDerivedStateFromError = createLifecycleChecker("getDerivedStateFromEr
|
|
|
341
354
|
* @deprecated Class components are legacy. This function exists only to support legacy rules.
|
|
342
355
|
*/
|
|
343
356
|
function isRenderMethodLike(node) {
|
|
344
|
-
return
|
|
357
|
+
return Check.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name.startsWith("render") && isOneOf([AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.ClassExpression])(node.parent.parent);
|
|
345
358
|
}
|
|
346
359
|
function isRenderMethodCallback(node) {
|
|
347
360
|
const parent = node.parent;
|
|
@@ -354,7 +367,7 @@ function isRenderMethodCallback(node) {
|
|
|
354
367
|
*/
|
|
355
368
|
function isThisSetStateCall(node) {
|
|
356
369
|
const { callee } = node;
|
|
357
|
-
return callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
370
|
+
return callee.type === AST_NODE_TYPES.MemberExpression && Check.thisExpression(callee.object) && callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "setState";
|
|
358
371
|
}
|
|
359
372
|
/**
|
|
360
373
|
* @param node The assignment expression node to check.
|
|
@@ -362,7 +375,7 @@ function isThisSetStateCall(node) {
|
|
|
362
375
|
*/
|
|
363
376
|
function isAssignmentToThisState(node) {
|
|
364
377
|
const { left } = node;
|
|
365
|
-
return left.type === AST_NODE_TYPES.MemberExpression &&
|
|
378
|
+
return left.type === AST_NODE_TYPES.MemberExpression && Check.thisExpression(left.object) && Extract.propertyName(left.property) === "state";
|
|
366
379
|
}
|
|
367
380
|
|
|
368
381
|
//#endregion
|
|
@@ -379,9 +392,9 @@ function getClassComponentCollector(context) {
|
|
|
379
392
|
const getText = (n) => context.sourceCode.getText(n);
|
|
380
393
|
const collect = (node) => {
|
|
381
394
|
if (!isClassComponent(node)) return;
|
|
382
|
-
const id =
|
|
395
|
+
const id = getClassId(node);
|
|
383
396
|
const key = ulid();
|
|
384
|
-
const name = id == null ? null :
|
|
397
|
+
const name = id == null ? null : Extract.fullyQualifiedName(id, getText);
|
|
385
398
|
components.set(key, {
|
|
386
399
|
id,
|
|
387
400
|
key,
|
|
@@ -403,6 +416,137 @@ function getClassComponentCollector(context) {
|
|
|
403
416
|
};
|
|
404
417
|
}
|
|
405
418
|
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/function.ts
|
|
421
|
+
/**
|
|
422
|
+
* Gets the static identifier of a function AST node.
|
|
423
|
+
*
|
|
424
|
+
* @remarks
|
|
425
|
+
* For function declarations this is straightforward. For anonymous function
|
|
426
|
+
* expressions it is more complex. This function roughly detects the same AST
|
|
427
|
+
* nodes as the ECMAScript spec's `IsAnonymousFunctionDefinition()` with some
|
|
428
|
+
* exceptions to better fit our use case.
|
|
429
|
+
*
|
|
430
|
+
* Ported from {@link https://github.com/facebook/react/blob/bb8a76c6cc77ea2976d690ea09f5a1b3d9b1792a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts#L860 | RulesOfHooks.ts}
|
|
431
|
+
*
|
|
432
|
+
* @param node - The function node to analyze.
|
|
433
|
+
* @returns The identifier node if found, `null` otherwise.
|
|
434
|
+
*/
|
|
435
|
+
function getFunctionId(node) {
|
|
436
|
+
switch (true) {
|
|
437
|
+
case "id" in node && node.id != null: return node.id;
|
|
438
|
+
case node.parent.type === AST_NODE_TYPES.VariableDeclarator && node.parent.init === node: return node.parent.id;
|
|
439
|
+
case node.parent.type === AST_NODE_TYPES.AssignmentExpression && node.parent.right === node && node.parent.operator === "=": return node.parent.left;
|
|
440
|
+
case node.parent.type === AST_NODE_TYPES.Property && node.parent.value === node && !node.parent.computed: return node.parent.key;
|
|
441
|
+
case Check.isMethodOrProperty(node.parent) && node.parent.value === node: return node.parent.key;
|
|
442
|
+
case node.parent.type === AST_NODE_TYPES.AssignmentPattern && node.parent.right === node: return node.parent.left;
|
|
443
|
+
case node.parent.type === AST_NODE_TYPES.ConditionalExpression: return getFunctionId(node.parent);
|
|
444
|
+
case Check.isTypeAssertionExpression(node.parent): return getFunctionId(node.parent);
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Identifies the initialization path of a function node in the AST.
|
|
450
|
+
*
|
|
451
|
+
* @param node - The function node to analyze.
|
|
452
|
+
* @returns The function initialization path or `null` if not identifiable.
|
|
453
|
+
*/
|
|
454
|
+
function getFunctionInitPath(node) {
|
|
455
|
+
if (node.type === AST_NODE_TYPES.FunctionDeclaration) return [node];
|
|
456
|
+
let parent = node.parent;
|
|
457
|
+
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
458
|
+
switch (true) {
|
|
459
|
+
case parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
460
|
+
parent.parent,
|
|
461
|
+
parent,
|
|
462
|
+
node
|
|
463
|
+
];
|
|
464
|
+
case parent.type === AST_NODE_TYPES.CallExpression && parent.parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
465
|
+
parent.parent.parent,
|
|
466
|
+
parent.parent,
|
|
467
|
+
parent,
|
|
468
|
+
node
|
|
469
|
+
];
|
|
470
|
+
case parent.type === AST_NODE_TYPES.CallExpression && parent.parent.type === AST_NODE_TYPES.CallExpression && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
471
|
+
parent.parent.parent.parent,
|
|
472
|
+
parent.parent.parent,
|
|
473
|
+
parent.parent,
|
|
474
|
+
parent,
|
|
475
|
+
node
|
|
476
|
+
];
|
|
477
|
+
case parent.type === AST_NODE_TYPES.Property && parent.parent.type === AST_NODE_TYPES.ObjectExpression && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator: return [
|
|
478
|
+
parent.parent.parent.parent,
|
|
479
|
+
parent.parent.parent,
|
|
480
|
+
parent.parent,
|
|
481
|
+
parent,
|
|
482
|
+
node
|
|
483
|
+
];
|
|
484
|
+
case parent.type === AST_NODE_TYPES.MethodDefinition: return [
|
|
485
|
+
parent.parent.parent,
|
|
486
|
+
parent.parent,
|
|
487
|
+
parent,
|
|
488
|
+
node
|
|
489
|
+
];
|
|
490
|
+
case parent.type === AST_NODE_TYPES.PropertyDefinition: return [
|
|
491
|
+
parent.parent.parent,
|
|
492
|
+
parent.parent,
|
|
493
|
+
parent,
|
|
494
|
+
node
|
|
495
|
+
];
|
|
496
|
+
}
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Checks if a specific function call exists in the function initialization path.
|
|
501
|
+
*
|
|
502
|
+
* @param callName - The name of the call to check for (e.g., "memo", "forwardRef").
|
|
503
|
+
* @param initPath - The function initialization path to search in.
|
|
504
|
+
* @returns `true` if the call exists in the path, `false` otherwise.
|
|
505
|
+
*/
|
|
506
|
+
function isFunctionHasCallInInitPath(callName, initPath) {
|
|
507
|
+
return initPath.some((node) => {
|
|
508
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
509
|
+
const { callee } = node;
|
|
510
|
+
if (callee.type === AST_NODE_TYPES.Identifier) return callee.name === callName;
|
|
511
|
+
if (callee.type === AST_NODE_TYPES.MemberExpression && "name" in callee.property) return callee.property.name === callName;
|
|
512
|
+
return false;
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Checks if a function is empty.
|
|
517
|
+
*
|
|
518
|
+
* @param node - The function node to check.
|
|
519
|
+
* @returns `true` if the function is empty, `false` otherwise.
|
|
520
|
+
*/
|
|
521
|
+
function isFunctionEmpty(node) {
|
|
522
|
+
return node.body.type === AST_NODE_TYPES.BlockStatement && node.body.body.length === 0;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Gets all directive expression statements from the top of a function body.
|
|
526
|
+
*
|
|
527
|
+
* @param node - The function AST node.
|
|
528
|
+
* @returns An array of directive expression statements.
|
|
529
|
+
*/
|
|
530
|
+
function getFunctionDirectives(node) {
|
|
531
|
+
const directives = [];
|
|
532
|
+
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return directives;
|
|
533
|
+
for (const stmt of node.body.body) {
|
|
534
|
+
if (!Check.directive(stmt)) continue;
|
|
535
|
+
directives.push(stmt);
|
|
536
|
+
}
|
|
537
|
+
return directives;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Checks if a directive with the given name exists in the function directives.
|
|
541
|
+
*
|
|
542
|
+
* @param node - The function AST node.
|
|
543
|
+
* @param name - The directive name to check (e.g., "use memo", "use no memo").
|
|
544
|
+
* @returns `true` if the directive exists, `false` otherwise.
|
|
545
|
+
*/
|
|
546
|
+
function isFunctionHasDirective(node, name) {
|
|
547
|
+
return getFunctionDirectives(node).some((d) => d.directive === name);
|
|
548
|
+
}
|
|
549
|
+
|
|
406
550
|
//#endregion
|
|
407
551
|
//#region src/function-component.ts
|
|
408
552
|
/**
|
|
@@ -423,8 +567,8 @@ const FunctionComponentFlag = {
|
|
|
423
567
|
*/
|
|
424
568
|
function getFunctionComponentFlagFromInitPath(initPath) {
|
|
425
569
|
let flag = FunctionComponentFlag.None;
|
|
426
|
-
if (initPath != null &&
|
|
427
|
-
if (initPath != null &&
|
|
570
|
+
if (initPath != null && isFunctionHasCallInInitPath("memo", initPath)) flag |= FunctionComponentFlag.Memo;
|
|
571
|
+
if (initPath != null && isFunctionHasCallInInitPath("forwardRef", initPath)) flag |= FunctionComponentFlag.ForwardRef;
|
|
428
572
|
return flag;
|
|
429
573
|
}
|
|
430
574
|
/**
|
|
@@ -444,9 +588,9 @@ function isFunctionComponentWrapperCall(context, node) {
|
|
|
444
588
|
* @returns `true` if the node is a callback function passed to a component wrapper
|
|
445
589
|
*/
|
|
446
590
|
function isFunctionComponentWrapperCallback(context, node) {
|
|
447
|
-
if (!
|
|
591
|
+
if (!Check.isFunction(node)) return false;
|
|
448
592
|
let parent = node.parent;
|
|
449
|
-
while (
|
|
593
|
+
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
450
594
|
if (parent.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
451
595
|
return isFunctionComponentWrapperCall(context, parent);
|
|
452
596
|
}
|
|
@@ -457,10 +601,10 @@ function isFunctionComponentWrapperCallback(context, node) {
|
|
|
457
601
|
* @internal
|
|
458
602
|
*/
|
|
459
603
|
function getFunctionComponentId(context, node) {
|
|
460
|
-
const functionId =
|
|
604
|
+
const functionId = getFunctionId(node);
|
|
461
605
|
if (functionId != null) return functionId;
|
|
462
606
|
let parent = node.parent;
|
|
463
|
-
while (
|
|
607
|
+
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
464
608
|
if (parent.type === AST_NODE_TYPES.CallExpression && isFunctionComponentWrapperCall(context, parent) && parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.id;
|
|
465
609
|
if (parent.type === AST_NODE_TYPES.CallExpression && isFunctionComponentWrapperCall(context, parent) && parent.parent.type === AST_NODE_TYPES.CallExpression && isFunctionComponentWrapperCall(context, parent.parent) && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator) return parent.parent.parent.id;
|
|
466
610
|
return null;
|
|
@@ -526,15 +670,15 @@ function isFunctionComponentDefinition(context, node, hint) {
|
|
|
526
670
|
case isRenderMethodCallback(node): return false;
|
|
527
671
|
}
|
|
528
672
|
let parent = node.parent;
|
|
529
|
-
while (
|
|
673
|
+
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
530
674
|
switch (true) {
|
|
531
|
-
case
|
|
675
|
+
case isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && parent.type === AST_NODE_TYPES.Property && parent.parent.type === AST_NODE_TYPES.ObjectExpression:
|
|
532
676
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsObjectMethod) return false;
|
|
533
677
|
break;
|
|
534
|
-
case
|
|
678
|
+
case isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && parent.type === AST_NODE_TYPES.MethodDefinition:
|
|
535
679
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsClassMethod) return false;
|
|
536
680
|
break;
|
|
537
|
-
case
|
|
681
|
+
case isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(node) && parent.type === AST_NODE_TYPES.Property:
|
|
538
682
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsClassProperty) return false;
|
|
539
683
|
break;
|
|
540
684
|
case parent.type === AST_NODE_TYPES.ArrayPattern:
|
|
@@ -549,11 +693,11 @@ function isFunctionComponentDefinition(context, node, hint) {
|
|
|
549
693
|
case parent.type === AST_NODE_TYPES.CallExpression && parent.callee.type === AST_NODE_TYPES.MemberExpression && parent.callee.property.type === AST_NODE_TYPES.Identifier && parent.callee.property.name === "flatMap":
|
|
550
694
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback) return false;
|
|
551
695
|
break;
|
|
552
|
-
case parent.type === AST_NODE_TYPES.CallExpression &&
|
|
696
|
+
case parent.type === AST_NODE_TYPES.CallExpression && getFunctionId(node) == null && !isFunctionComponentWrapperCall(context, parent) && !isCreateElementCall(context, parent):
|
|
553
697
|
if (hint & FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArbitraryCallExpressionCallback) return false;
|
|
554
698
|
break;
|
|
555
699
|
}
|
|
556
|
-
const significantParent =
|
|
700
|
+
const significantParent = Traverse.findParent(node, isOneOf([
|
|
557
701
|
AST_NODE_TYPES.JSXExpressionContainer,
|
|
558
702
|
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
559
703
|
AST_NODE_TYPES.FunctionExpression,
|
|
@@ -616,7 +760,7 @@ function isHookId(id) {
|
|
|
616
760
|
*/
|
|
617
761
|
function isHookDefinition(node) {
|
|
618
762
|
if (node == null) return false;
|
|
619
|
-
const id =
|
|
763
|
+
const id = getFunctionId(node);
|
|
620
764
|
switch (id?.type) {
|
|
621
765
|
case AST_NODE_TYPES.Identifier: return isHookName(id.name);
|
|
622
766
|
case AST_NODE_TYPES.MemberExpression: return "name" in id.property && isHookName(id.property.name);
|
|
@@ -661,7 +805,7 @@ function isUseStateLikeCall(node, additionalStateHooks = { test: constFalse }) {
|
|
|
661
805
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
662
806
|
switch (true) {
|
|
663
807
|
case node.callee.type === AST_NODE_TYPES.Identifier: return node.callee.name === "useState" || additionalStateHooks.test(node.callee.name);
|
|
664
|
-
case node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier: return
|
|
808
|
+
case node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier: return Extract.propertyName(node.callee.property) === "useState" || additionalStateHooks.test(node.callee.property.name);
|
|
665
809
|
}
|
|
666
810
|
return false;
|
|
667
811
|
}
|
|
@@ -679,9 +823,9 @@ function isUseEffectSetupCallback(node) {
|
|
|
679
823
|
*/
|
|
680
824
|
function isUseEffectCleanupCallback(node) {
|
|
681
825
|
if (node == null) return false;
|
|
682
|
-
const returnStatement =
|
|
683
|
-
const enclosingFunction =
|
|
684
|
-
if (enclosingFunction !==
|
|
826
|
+
const returnStatement = Traverse.findParent(node, is(AST_NODE_TYPES.ReturnStatement));
|
|
827
|
+
const enclosingFunction = Traverse.findParent(node, Check.isFunction);
|
|
828
|
+
if (enclosingFunction !== Traverse.findParent(returnStatement, Check.isFunction)) return false;
|
|
685
829
|
return isUseEffectSetupCallback(enclosingFunction);
|
|
686
830
|
}
|
|
687
831
|
|
|
@@ -701,13 +845,13 @@ function getFunctionComponentCollector(context, options = {}) {
|
|
|
701
845
|
const getCurrentEntry = () => functionEntries.at(-1) ?? null;
|
|
702
846
|
const onFunctionEnter = (node) => {
|
|
703
847
|
const key = ulid();
|
|
704
|
-
const exp =
|
|
848
|
+
const exp = Traverse.findParent(node, (n) => n.type === AST_NODE_TYPES.ExportDefaultDeclaration);
|
|
705
849
|
const isExportDefault = exp != null;
|
|
706
|
-
const isExportDefaultDeclaration = exp != null &&
|
|
850
|
+
const isExportDefaultDeclaration = exp != null && Extract.unwrapped(exp.declaration) === node;
|
|
707
851
|
const id = getFunctionComponentId(context, node);
|
|
708
|
-
const name = id == null ? null :
|
|
709
|
-
const initPath =
|
|
710
|
-
const directives =
|
|
852
|
+
const name = id == null ? null : Extract.fullyQualifiedName(id, getText);
|
|
853
|
+
const initPath = getFunctionInitPath(node);
|
|
854
|
+
const directives = getFunctionDirectives(node);
|
|
711
855
|
const entry = {
|
|
712
856
|
id,
|
|
713
857
|
key,
|
|
@@ -749,7 +893,7 @@ function getFunctionComponentCollector(context, options = {}) {
|
|
|
749
893
|
if (!components.has(entry.key) && !isJsxLike(context, body, hint)) return;
|
|
750
894
|
components.set(entry.key, entry);
|
|
751
895
|
},
|
|
752
|
-
...collectDisplayName ? { [
|
|
896
|
+
...collectDisplayName ? { [Select.displayNameAssignment](node) {
|
|
753
897
|
const { left, right } = node;
|
|
754
898
|
if (left.type !== AST_NODE_TYPES.MemberExpression) return;
|
|
755
899
|
const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : null;
|
|
@@ -791,13 +935,13 @@ function getHookCollector(context) {
|
|
|
791
935
|
const getText = (n) => context.sourceCode.getText(n);
|
|
792
936
|
const getCurrentEntry = () => functionEntries.at(-1) ?? null;
|
|
793
937
|
const onFunctionEnter = (node) => {
|
|
794
|
-
const id =
|
|
938
|
+
const id = getFunctionId(node);
|
|
795
939
|
const key = ulid();
|
|
796
940
|
const entry = {
|
|
797
941
|
id,
|
|
798
942
|
key,
|
|
799
943
|
kind: "hook",
|
|
800
|
-
name: id == null ? null :
|
|
944
|
+
name: id == null ? null : Extract.fullyQualifiedName(id, getText),
|
|
801
945
|
directives: [],
|
|
802
946
|
flag: 0n,
|
|
803
947
|
hint: 0n,
|
|
@@ -989,4 +1133,4 @@ function getTypeVariants(types) {
|
|
|
989
1133
|
}
|
|
990
1134
|
|
|
991
1135
|
//#endregion
|
|
992
|
-
export { DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, REACT_BUILTIN_HOOK_NAMES, getClassComponentCollector, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFunctionComponentDefinition, isFunctionComponentName, isFunctionComponentNameLoose, isFunctionComponentWrapperCall, isFunctionComponentWrapperCallback, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isStringType, isThisSetStateCall, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUse, isUseActionState, isUseActionStateCall, isUseCall, isUseCallback, isUseCallbackCall, isUseContext, isUseContextCall, isUseDebugValue, isUseDebugValueCall, isUseDeferredValue, isUseDeferredValueCall, isUseEffect, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatus, isUseFormStatusCall, isUseId, isUseIdCall, isUseImperativeHandle, isUseImperativeHandleCall, isUseInsertionEffect, isUseInsertionEffectCall, isUseLayoutEffect, isUseLayoutEffectCall, isUseMemo, isUseMemoCall, isUseOptimistic, isUseOptimisticCall, isUseReducer, isUseReducerCall, isUseRef, isUseRefCall, isUseState, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStore, isUseSyncExternalStoreCall, isUseTransition, isUseTransitionCall };
|
|
1136
|
+
export { DEFAULT_COMPONENT_DETECTION_HINT, FunctionComponentDetectionHint, FunctionComponentFlag, REACT_BUILTIN_HOOK_NAMES, getClassComponentCollector, getClassId, getFullyQualifiedNameEx, getFunctionComponentCollector, getFunctionComponentFlagFromInitPath, getFunctionComponentId, getFunctionDirectives, getFunctionId, getFunctionInitPath, getHookCollector, getTypeVariants, isAPI, isAPICall, isAPIFromReact, isAPIFromReactNative, isAnyType, isAssignmentToThisState, isBigIntType, isBooleanLiteralType, isBooleanType, isCaptureOwnerStack, isCaptureOwnerStackCall, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isClassComponentLoose, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUnmount, isComponentWillUpdate, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isEnumType, isFalseLiteralType, isFalsyBigIntType, isFalsyNumberType, isFalsyStringType, isForwardRef, isForwardRefCall, isFunctionComponentDefinition, isFunctionComponentName, isFunctionComponentNameLoose, isFunctionComponentWrapperCall, isFunctionComponentWrapperCallback, isFunctionEmpty, isFunctionHasCallInInitPath, isFunctionHasDirective, isFunctionWithLooseComponentName, isGetChildContext, isGetDefaultProps, isGetDerivedStateFromError, isGetDerivedStateFromProps, isGetInitialState, isGetSnapshotBeforeUpdate, isHookCall, isHookDefinition, isHookId, isHookName, isLazy, isLazyCall, isMemo, isMemoCall, isNeverType, isNullishType, isNumberType, isObjectType, isPureComponent, isRender, isRenderMethodCallback, isRenderMethodLike, isShouldComponentUpdate, isStringType, isThisSetStateCall, isTrueLiteralType, isTruthyBigIntType, isTruthyNumberType, isTruthyStringType, isUnknownType, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUse, isUseActionState, isUseActionStateCall, isUseCall, isUseCallback, isUseCallbackCall, isUseContext, isUseContextCall, isUseDebugValue, isUseDebugValueCall, isUseDeferredValue, isUseDeferredValueCall, isUseEffect, isUseEffectCall, isUseEffectCleanupCallback, isUseEffectLikeCall, isUseEffectSetupCallback, isUseFormStatus, isUseFormStatusCall, isUseId, isUseIdCall, isUseImperativeHandle, isUseImperativeHandleCall, isUseInsertionEffect, isUseInsertionEffectCall, isUseLayoutEffect, isUseLayoutEffectCall, isUseMemo, isUseMemoCall, isUseOptimistic, isUseOptimisticCall, isUseReducer, isUseReducerCall, isUseRef, isUseRefCall, isUseState, isUseStateCall, isUseStateLikeCall, isUseSyncExternalStore, isUseSyncExternalStoreCall, isUseTransition, isUseTransitionCall };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-react/core",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.2-beta.0",
|
|
4
4
|
"description": "ESLint React's ESLint utility module for static analysis of React core APIs and patterns.",
|
|
5
5
|
"homepage": "https://github.com/Rel1cx/eslint-react",
|
|
6
6
|
"bugs": {
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"@typescript-eslint/utils": "^8.58.1",
|
|
36
36
|
"ts-pattern": "^5.9.0",
|
|
37
37
|
"ulid": "^3.0.2",
|
|
38
|
-
"@eslint-react/ast": "5.2.
|
|
39
|
-
"@eslint-react/
|
|
40
|
-
"@eslint-react/
|
|
41
|
-
"@eslint-react/
|
|
42
|
-
"@eslint-react/var": "5.2.
|
|
38
|
+
"@eslint-react/ast": "5.2.2-beta.0",
|
|
39
|
+
"@eslint-react/jsx": "5.2.2-beta.0",
|
|
40
|
+
"@eslint-react/shared": "5.2.2-beta.0",
|
|
41
|
+
"@eslint-react/eslint": "5.2.2-beta.0",
|
|
42
|
+
"@eslint-react/var": "5.2.2-beta.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@typescript-eslint/typescript-estree": "^8.58.1",
|