@eslint-react/core 1.24.0-next.9 → 1.24.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.mts +26 -14
- package/dist/index.d.ts +26 -14
- package/dist/index.js +61 -68
- package/dist/index.mjs +55 -62
- package/package.json +7 -7
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as AST from '@eslint-react/ast';
|
|
2
2
|
import * as _eslint_react_shared from '@eslint-react/shared';
|
|
3
|
-
import { RuleContext
|
|
3
|
+
import { RuleContext } from '@eslint-react/shared';
|
|
4
4
|
import { TSESTree } from '@typescript-eslint/types';
|
|
5
5
|
import { _ } from '@eslint-react/eff';
|
|
6
6
|
import { TSESTree as TSESTree$1 } from '@typescript-eslint/utils';
|
|
@@ -82,6 +82,13 @@ interface ComponentCollectorOptions {
|
|
|
82
82
|
collectDisplayName?: boolean;
|
|
83
83
|
collectHookCalls?: boolean;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Get a ctx and listeners for the rule to collect function components
|
|
87
|
+
* @param context The ESLint rule context
|
|
88
|
+
* @param hint The hint to use
|
|
89
|
+
* @param options The options to use
|
|
90
|
+
* @returns The component collector
|
|
91
|
+
*/
|
|
85
92
|
declare function useComponentCollector(context: RuleContext, hint?: bigint, options?: ComponentCollectorOptions): {
|
|
86
93
|
readonly ctx: {
|
|
87
94
|
readonly getAllComponents: (node: TSESTree.Program) => Map<string, ERFunctionComponent>;
|
|
@@ -101,9 +108,6 @@ declare function useComponentCollector(context: RuleContext, hint?: bigint, opti
|
|
|
101
108
|
readonly listeners: {
|
|
102
109
|
readonly "ReturnStatement[type]": (node: TSESTree.ReturnStatement) => void;
|
|
103
110
|
readonly "CallExpression[type]:exit"?: (node: TSESTree.CallExpression) => void;
|
|
104
|
-
readonly "AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']"?: (node: TSESTree.AssignmentExpression & {
|
|
105
|
-
left: TSESTree.MemberExpression;
|
|
106
|
-
}) => void;
|
|
107
111
|
readonly ":function[type]": (node: AST.TSESTreeFunction) => void;
|
|
108
112
|
readonly ":function[type]:exit": () => {
|
|
109
113
|
key: string;
|
|
@@ -115,6 +119,10 @@ declare function useComponentCollector(context: RuleContext, hint?: bigint, opti
|
|
|
115
119
|
};
|
|
116
120
|
};
|
|
117
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Get a ctx and listeners for the rule to collect class components
|
|
124
|
+
* @returns The context and listeners for the rule
|
|
125
|
+
*/
|
|
118
126
|
declare function useComponentCollectorLegacy(): {
|
|
119
127
|
readonly ctx: {
|
|
120
128
|
readonly getAllComponents: (node: TSESTree$1.Program) => Map<string, ERClassComponent>;
|
|
@@ -125,6 +133,17 @@ declare function useComponentCollectorLegacy(): {
|
|
|
125
133
|
};
|
|
126
134
|
};
|
|
127
135
|
|
|
136
|
+
/**
|
|
137
|
+
* The ESQuery selector for a component display name assignment expression
|
|
138
|
+
*/
|
|
139
|
+
declare const COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR: string;
|
|
140
|
+
/**
|
|
141
|
+
* Check if the node is a component display name assignment expression
|
|
142
|
+
* @param node The AST node
|
|
143
|
+
* @returns `true` if the node is a component display name assignment
|
|
144
|
+
*/
|
|
145
|
+
declare function isComponentDisplayNameAssignment(node: TSESTree.Node | _): node is TSESTree.AssignmentExpression;
|
|
146
|
+
|
|
128
147
|
declare function getFunctionComponentIdentifier(node: AST.TSESTreeFunction, context: RuleContext): TSESTree.Identifier | TSESTree.Identifier[] | _;
|
|
129
148
|
|
|
130
149
|
type ERComponentKind = "classComponent" | "functionComponent";
|
|
@@ -155,9 +174,6 @@ declare function isFunctionOfRenderMethod(node: AST.TSESTreeFunction): boolean;
|
|
|
155
174
|
*/
|
|
156
175
|
declare function isInsideRenderMethod(node: TSESTree.Node): boolean;
|
|
157
176
|
|
|
158
|
-
type EREffectMethodKind = "useEffect" | "useInsertionEffect" | "useLayoutEffect";
|
|
159
|
-
type ERLifecycleMethodKind = "componentDidMount" | "componentWillUnmount";
|
|
160
|
-
|
|
161
177
|
declare const RE_COMPONENT_NAME: RegExp;
|
|
162
178
|
declare function getComponentNameFromIdentifier(node: TSESTree.Identifier | TSESTree.Identifier[] | _): string | undefined;
|
|
163
179
|
declare function isComponentName(name: string): boolean;
|
|
@@ -241,13 +257,11 @@ declare function isClassComponent(node: TSESTree.Node): node is AST.TSESTreeClas
|
|
|
241
257
|
*/
|
|
242
258
|
declare function isPureComponent(node: TSESTree.Node): boolean;
|
|
243
259
|
|
|
244
|
-
type EREffectKind = "
|
|
260
|
+
type EREffectKind = "useEffect" | "useLayoutEffect" | "useInsertionEffect";
|
|
245
261
|
|
|
246
262
|
declare function isFunctionOfUseEffectSetup(node: TSESTree.Node | _): boolean | undefined;
|
|
247
263
|
declare function isFunctionOfUseEffectCleanup(node: TSESTree.Node): boolean | undefined;
|
|
248
264
|
|
|
249
|
-
declare function getElementNameOnJsxAndHtml(node: TSESTree.JSXOpeningElement, context: RuleContext, polymorphicPropName?: string, additionalComponents?: CustomComponentNormalized[]): [string, string];
|
|
250
|
-
|
|
251
265
|
/**
|
|
252
266
|
* Determines whether inside `createElement`'s props.
|
|
253
267
|
* @param node The AST node to check
|
|
@@ -321,8 +335,6 @@ interface ERSemanticEntry {
|
|
|
321
335
|
phase: ERPhaseKind;
|
|
322
336
|
}
|
|
323
337
|
|
|
324
|
-
declare const getId: () => string;
|
|
325
|
-
|
|
326
338
|
/**
|
|
327
339
|
* Checks if the given node is a call expression to the given function or method of the pragma
|
|
328
340
|
* @param name The name of the function or method to check
|
|
@@ -351,7 +363,7 @@ type IsCallFromReactMember = {
|
|
|
351
363
|
};
|
|
352
364
|
declare function isCallFromReactMember(pragmaMemberName: string, name: string): IsCallFromReactMember;
|
|
353
365
|
|
|
354
|
-
declare function isInitializedFromReact(name: string,
|
|
366
|
+
declare function isInitializedFromReact(name: string, importSource: string, initialScope: Scope): boolean;
|
|
355
367
|
|
|
356
368
|
/**********************************************
|
|
357
369
|
* DO NOT MODIFY THIS FILE MANUALLY *
|
|
@@ -2575,4 +2587,4 @@ declare const isMemoCall: {
|
|
|
2575
2587
|
(node: Node, context: _eslint_react_shared.RuleContext): node is CallExpression;
|
|
2576
2588
|
};
|
|
2577
2589
|
|
|
2578
|
-
export { type ComponentCollectorOptions, DEFAULT_COMPONENT_HINT, type ERClassComponent, ERClassComponentFlag, type ERComponent, ERComponentHint, type ERComponentKind, type EREffectKind, type
|
|
2590
|
+
export { COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR, type ComponentCollectorOptions, DEFAULT_COMPONENT_HINT, type ERClassComponent, ERClassComponentFlag, type ERComponent, ERComponentHint, type ERComponentKind, type EREffectKind, type EREffectPhaseKind, type ERFunctionComponent, ERFunctionComponentFlag, type ERHook, type ERHookKind, type ERLifecyclePhaseKind, type ERPhaseKind, ERPhaseRelevance, type ERSemanticEntry, type ERSemanticNode, type ERStateKind, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier, getFunctionComponentIdentifier, hasNoneOrValidComponentName, isCallFromReact, isCallFromReactMember, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDisplayNameAssignment, isComponentName, isComponentWillUnmount, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFromReact, isFromReactMember, isFunctionOfComponentDidMount, isFunctionOfComponentWillUnmount, isFunctionOfRenderMethod, isFunctionOfUseEffectCleanup, isFunctionOfUseEffectSetup, isGetDerivedStateFromError, isGetDerivedStateFromProps, isInitializedFromReact, isInsideCreateElementProps, isInsideReactHook, isInsideRenderMethod, isInversePhase, isMemo, isMemoCall, isPureComponent, isReactAPICallWithName, isReactAPIWithName, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookName, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isThisSetState, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCallLoose, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as AST from '@eslint-react/ast';
|
|
2
2
|
import * as _eslint_react_shared from '@eslint-react/shared';
|
|
3
|
-
import { RuleContext
|
|
3
|
+
import { RuleContext } from '@eslint-react/shared';
|
|
4
4
|
import { TSESTree } from '@typescript-eslint/types';
|
|
5
5
|
import { _ } from '@eslint-react/eff';
|
|
6
6
|
import { TSESTree as TSESTree$1 } from '@typescript-eslint/utils';
|
|
@@ -82,6 +82,13 @@ interface ComponentCollectorOptions {
|
|
|
82
82
|
collectDisplayName?: boolean;
|
|
83
83
|
collectHookCalls?: boolean;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Get a ctx and listeners for the rule to collect function components
|
|
87
|
+
* @param context The ESLint rule context
|
|
88
|
+
* @param hint The hint to use
|
|
89
|
+
* @param options The options to use
|
|
90
|
+
* @returns The component collector
|
|
91
|
+
*/
|
|
85
92
|
declare function useComponentCollector(context: RuleContext, hint?: bigint, options?: ComponentCollectorOptions): {
|
|
86
93
|
readonly ctx: {
|
|
87
94
|
readonly getAllComponents: (node: TSESTree.Program) => Map<string, ERFunctionComponent>;
|
|
@@ -101,9 +108,6 @@ declare function useComponentCollector(context: RuleContext, hint?: bigint, opti
|
|
|
101
108
|
readonly listeners: {
|
|
102
109
|
readonly "ReturnStatement[type]": (node: TSESTree.ReturnStatement) => void;
|
|
103
110
|
readonly "CallExpression[type]:exit"?: (node: TSESTree.CallExpression) => void;
|
|
104
|
-
readonly "AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']"?: (node: TSESTree.AssignmentExpression & {
|
|
105
|
-
left: TSESTree.MemberExpression;
|
|
106
|
-
}) => void;
|
|
107
111
|
readonly ":function[type]": (node: AST.TSESTreeFunction) => void;
|
|
108
112
|
readonly ":function[type]:exit": () => {
|
|
109
113
|
key: string;
|
|
@@ -115,6 +119,10 @@ declare function useComponentCollector(context: RuleContext, hint?: bigint, opti
|
|
|
115
119
|
};
|
|
116
120
|
};
|
|
117
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Get a ctx and listeners for the rule to collect class components
|
|
124
|
+
* @returns The context and listeners for the rule
|
|
125
|
+
*/
|
|
118
126
|
declare function useComponentCollectorLegacy(): {
|
|
119
127
|
readonly ctx: {
|
|
120
128
|
readonly getAllComponents: (node: TSESTree$1.Program) => Map<string, ERClassComponent>;
|
|
@@ -125,6 +133,17 @@ declare function useComponentCollectorLegacy(): {
|
|
|
125
133
|
};
|
|
126
134
|
};
|
|
127
135
|
|
|
136
|
+
/**
|
|
137
|
+
* The ESQuery selector for a component display name assignment expression
|
|
138
|
+
*/
|
|
139
|
+
declare const COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR: string;
|
|
140
|
+
/**
|
|
141
|
+
* Check if the node is a component display name assignment expression
|
|
142
|
+
* @param node The AST node
|
|
143
|
+
* @returns `true` if the node is a component display name assignment
|
|
144
|
+
*/
|
|
145
|
+
declare function isComponentDisplayNameAssignment(node: TSESTree.Node | _): node is TSESTree.AssignmentExpression;
|
|
146
|
+
|
|
128
147
|
declare function getFunctionComponentIdentifier(node: AST.TSESTreeFunction, context: RuleContext): TSESTree.Identifier | TSESTree.Identifier[] | _;
|
|
129
148
|
|
|
130
149
|
type ERComponentKind = "classComponent" | "functionComponent";
|
|
@@ -155,9 +174,6 @@ declare function isFunctionOfRenderMethod(node: AST.TSESTreeFunction): boolean;
|
|
|
155
174
|
*/
|
|
156
175
|
declare function isInsideRenderMethod(node: TSESTree.Node): boolean;
|
|
157
176
|
|
|
158
|
-
type EREffectMethodKind = "useEffect" | "useInsertionEffect" | "useLayoutEffect";
|
|
159
|
-
type ERLifecycleMethodKind = "componentDidMount" | "componentWillUnmount";
|
|
160
|
-
|
|
161
177
|
declare const RE_COMPONENT_NAME: RegExp;
|
|
162
178
|
declare function getComponentNameFromIdentifier(node: TSESTree.Identifier | TSESTree.Identifier[] | _): string | undefined;
|
|
163
179
|
declare function isComponentName(name: string): boolean;
|
|
@@ -241,13 +257,11 @@ declare function isClassComponent(node: TSESTree.Node): node is AST.TSESTreeClas
|
|
|
241
257
|
*/
|
|
242
258
|
declare function isPureComponent(node: TSESTree.Node): boolean;
|
|
243
259
|
|
|
244
|
-
type EREffectKind = "
|
|
260
|
+
type EREffectKind = "useEffect" | "useLayoutEffect" | "useInsertionEffect";
|
|
245
261
|
|
|
246
262
|
declare function isFunctionOfUseEffectSetup(node: TSESTree.Node | _): boolean | undefined;
|
|
247
263
|
declare function isFunctionOfUseEffectCleanup(node: TSESTree.Node): boolean | undefined;
|
|
248
264
|
|
|
249
|
-
declare function getElementNameOnJsxAndHtml(node: TSESTree.JSXOpeningElement, context: RuleContext, polymorphicPropName?: string, additionalComponents?: CustomComponentNormalized[]): [string, string];
|
|
250
|
-
|
|
251
265
|
/**
|
|
252
266
|
* Determines whether inside `createElement`'s props.
|
|
253
267
|
* @param node The AST node to check
|
|
@@ -321,8 +335,6 @@ interface ERSemanticEntry {
|
|
|
321
335
|
phase: ERPhaseKind;
|
|
322
336
|
}
|
|
323
337
|
|
|
324
|
-
declare const getId: () => string;
|
|
325
|
-
|
|
326
338
|
/**
|
|
327
339
|
* Checks if the given node is a call expression to the given function or method of the pragma
|
|
328
340
|
* @param name The name of the function or method to check
|
|
@@ -351,7 +363,7 @@ type IsCallFromReactMember = {
|
|
|
351
363
|
};
|
|
352
364
|
declare function isCallFromReactMember(pragmaMemberName: string, name: string): IsCallFromReactMember;
|
|
353
365
|
|
|
354
|
-
declare function isInitializedFromReact(name: string,
|
|
366
|
+
declare function isInitializedFromReact(name: string, importSource: string, initialScope: Scope): boolean;
|
|
355
367
|
|
|
356
368
|
/**********************************************
|
|
357
369
|
* DO NOT MODIFY THIS FILE MANUALLY *
|
|
@@ -2575,4 +2587,4 @@ declare const isMemoCall: {
|
|
|
2575
2587
|
(node: Node, context: _eslint_react_shared.RuleContext): node is CallExpression;
|
|
2576
2588
|
};
|
|
2577
2589
|
|
|
2578
|
-
export { type ComponentCollectorOptions, DEFAULT_COMPONENT_HINT, type ERClassComponent, ERClassComponentFlag, type ERComponent, ERComponentHint, type ERComponentKind, type EREffectKind, type
|
|
2590
|
+
export { COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR, type ComponentCollectorOptions, DEFAULT_COMPONENT_HINT, type ERClassComponent, ERClassComponentFlag, type ERComponent, ERComponentHint, type ERComponentKind, type EREffectKind, type EREffectPhaseKind, type ERFunctionComponent, ERFunctionComponentFlag, type ERHook, type ERHookKind, type ERLifecyclePhaseKind, type ERPhaseKind, ERPhaseRelevance, type ERSemanticEntry, type ERSemanticNode, type ERStateKind, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier, getFunctionComponentIdentifier, hasNoneOrValidComponentName, isCallFromReact, isCallFromReactMember, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDisplayNameAssignment, isComponentName, isComponentWillUnmount, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFromReact, isFromReactMember, isFunctionOfComponentDidMount, isFunctionOfComponentWillUnmount, isFunctionOfRenderMethod, isFunctionOfUseEffectCleanup, isFunctionOfUseEffectSetup, isGetDerivedStateFromError, isGetDerivedStateFromProps, isInitializedFromReact, isInsideCreateElementProps, isInsideReactHook, isInsideRenderMethod, isInversePhase, isMemo, isMemoCall, isPureComponent, isReactAPICallWithName, isReactAPIWithName, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookName, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isThisSetState, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCallLoose, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
var AST8 = require('@eslint-react/ast');
|
|
4
4
|
var eff = require('@eslint-react/eff');
|
|
5
|
-
var
|
|
5
|
+
var JSX3 = require('@eslint-react/jsx');
|
|
6
|
+
var shared = require('@eslint-react/shared');
|
|
6
7
|
var types = require('@typescript-eslint/types');
|
|
7
8
|
var VAR = require('@eslint-react/var');
|
|
8
|
-
var shared = require('@eslint-react/shared');
|
|
9
|
-
require('ts-pattern');
|
|
10
9
|
var birecord = require('birecord');
|
|
11
10
|
|
|
12
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -30,35 +29,12 @@ function _interopNamespace(e) {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
var AST8__namespace = /*#__PURE__*/_interopNamespace(AST8);
|
|
33
|
-
var
|
|
32
|
+
var JSX3__namespace = /*#__PURE__*/_interopNamespace(JSX3);
|
|
34
33
|
var VAR__namespace = /*#__PURE__*/_interopNamespace(VAR);
|
|
35
34
|
var birecord__default = /*#__PURE__*/_interopDefault(birecord);
|
|
36
35
|
|
|
37
36
|
// src/component/component-collector.ts
|
|
38
|
-
function
|
|
39
|
-
const name = JSX4__namespace.getElementName(node);
|
|
40
|
-
if (name === name.toLowerCase()) return [name, name];
|
|
41
|
-
const component = additionalComponents.findLast((c) => c.name === name || c.re.test(name));
|
|
42
|
-
if (component != null) return [name, component.as];
|
|
43
|
-
if (polymorphicPropName == null) return [name, name];
|
|
44
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
45
|
-
const attributeNode = JSX4__namespace.getAttributeNode(
|
|
46
|
-
polymorphicPropName,
|
|
47
|
-
initialScope,
|
|
48
|
-
node.attributes
|
|
49
|
-
);
|
|
50
|
-
if (attributeNode == null) return [name, name];
|
|
51
|
-
const polymorphicPropValue = VAR__namespace.toResolved(JSX4__namespace.getAttributeStaticValue(attributeNode, initialScope)).value;
|
|
52
|
-
if (typeof polymorphicPropValue === "string") {
|
|
53
|
-
return [name, polymorphicPropValue];
|
|
54
|
-
}
|
|
55
|
-
return [name, name];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// src/utils/get-id.ts
|
|
59
|
-
var id = 0n;
|
|
60
|
-
var getId = () => (id++).toString();
|
|
61
|
-
function isInitializedFromReact(name, initialScope, importSource = "react") {
|
|
37
|
+
function isInitializedFromReact(name, importSource, initialScope) {
|
|
62
38
|
return name.toLowerCase() === "react" || VAR__namespace.isInitializedFromSource(
|
|
63
39
|
name,
|
|
64
40
|
importSource,
|
|
@@ -76,12 +52,13 @@ function isFromReact(name) {
|
|
|
76
52
|
}
|
|
77
53
|
return node.name === name;
|
|
78
54
|
}
|
|
55
|
+
const importSource = settings.importSource ?? shared.DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
79
56
|
const initialScope = context.sourceCode.getScope(node);
|
|
80
57
|
if (node.type === types.AST_NODE_TYPES.MemberExpression) {
|
|
81
|
-
return node.object.type === types.AST_NODE_TYPES.Identifier && node.property.type === types.AST_NODE_TYPES.Identifier && node.property.name === name && isInitializedFromReact(node.object.name,
|
|
58
|
+
return node.object.type === types.AST_NODE_TYPES.Identifier && node.property.type === types.AST_NODE_TYPES.Identifier && node.property.name === name && isInitializedFromReact(node.object.name, importSource, initialScope);
|
|
82
59
|
}
|
|
83
60
|
if (node.name === name) {
|
|
84
|
-
return isInitializedFromReact(name,
|
|
61
|
+
return isInitializedFromReact(name, importSource, initialScope);
|
|
85
62
|
}
|
|
86
63
|
return false;
|
|
87
64
|
};
|
|
@@ -101,14 +78,15 @@ function isFromReactMember(memberName, name) {
|
|
|
101
78
|
}
|
|
102
79
|
return false;
|
|
103
80
|
}
|
|
81
|
+
const importSource = settings.importSource ?? shared.DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
104
82
|
const initialScope = context.sourceCode.getScope(node);
|
|
105
83
|
if (node.property.type !== types.AST_NODE_TYPES.Identifier || node.property.name !== name) {
|
|
106
84
|
return false;
|
|
107
85
|
}
|
|
108
86
|
if (node.object.type === types.AST_NODE_TYPES.Identifier && node.object.name === memberName) {
|
|
109
|
-
return isInitializedFromReact(node.object.name,
|
|
87
|
+
return isInitializedFromReact(node.object.name, importSource, initialScope);
|
|
110
88
|
}
|
|
111
|
-
if (node.object.type === types.AST_NODE_TYPES.MemberExpression && node.object.object.type === types.AST_NODE_TYPES.Identifier && isInitializedFromReact(node.object.object.name,
|
|
89
|
+
if (node.object.type === types.AST_NODE_TYPES.MemberExpression && node.object.object.type === types.AST_NODE_TYPES.Identifier && isInitializedFromReact(node.object.object.name, importSource, initialScope) && node.object.property.type === types.AST_NODE_TYPES.Identifier) {
|
|
112
90
|
return node.object.property.name === memberName;
|
|
113
91
|
}
|
|
114
92
|
return false;
|
|
@@ -191,8 +169,8 @@ function isReactHookName(name) {
|
|
|
191
169
|
// src/hook/is.ts
|
|
192
170
|
function isReactHook(node) {
|
|
193
171
|
if (node == null) return eff._;
|
|
194
|
-
const
|
|
195
|
-
return
|
|
172
|
+
const id = AST8__namespace.getFunctionIdentifier(node);
|
|
173
|
+
return id?.name != null && isReactHookName(id.name);
|
|
196
174
|
}
|
|
197
175
|
function isReactHookCall(node) {
|
|
198
176
|
if (node == null) return false;
|
|
@@ -210,13 +188,14 @@ function isReactHookCall(node) {
|
|
|
210
188
|
function isReactHookCallWithName(node, context) {
|
|
211
189
|
if (node == null) return eff.returnFalse;
|
|
212
190
|
const settings = shared.unsafeDecodeSettings(context.settings);
|
|
191
|
+
const importSource = settings.importSource ?? "react";
|
|
192
|
+
const initialScope = context.sourceCode.getScope(node);
|
|
213
193
|
return (name) => {
|
|
214
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
215
194
|
switch (true) {
|
|
216
195
|
case (node.callee.type === types.AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
217
|
-
return !settings.strictImportCheck || isInitializedFromReact(name,
|
|
196
|
+
return !settings.strictImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
218
197
|
case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
219
|
-
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name,
|
|
198
|
+
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
220
199
|
default:
|
|
221
200
|
return false;
|
|
222
201
|
}
|
|
@@ -237,13 +216,14 @@ function isReactHookCallWithNameLoose(node) {
|
|
|
237
216
|
}
|
|
238
217
|
function isReactHookCallWithNameAlias(name, context, alias) {
|
|
239
218
|
const settings = shared.unsafeDecodeSettings(context);
|
|
219
|
+
const importSource = settings.importSource ?? "react";
|
|
240
220
|
return (node) => {
|
|
241
221
|
const initialScope = context.sourceCode.getScope(node);
|
|
242
222
|
switch (true) {
|
|
243
223
|
case (node.callee.type === types.AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
244
|
-
return !settings.strictImportCheck || isInitializedFromReact(name,
|
|
224
|
+
return !settings.strictImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
245
225
|
case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
246
|
-
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name,
|
|
226
|
+
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
247
227
|
default:
|
|
248
228
|
return alias.some(isReactHookCallWithNameLoose(node));
|
|
249
229
|
}
|
|
@@ -287,13 +267,13 @@ function useHookCollector() {
|
|
|
287
267
|
const hooks = /* @__PURE__ */ new Map();
|
|
288
268
|
const fEntries = [];
|
|
289
269
|
const onFunctionEnter = (node) => {
|
|
290
|
-
const
|
|
291
|
-
const name =
|
|
270
|
+
const id = AST8__namespace.getFunctionIdentifier(node);
|
|
271
|
+
const name = id?.name;
|
|
292
272
|
if (name != null && isReactHookName(name)) {
|
|
293
|
-
const key = getId();
|
|
273
|
+
const key = shared.getId();
|
|
294
274
|
fEntries.push({ key, node });
|
|
295
275
|
hooks.set(key, {
|
|
296
|
-
id
|
|
276
|
+
id,
|
|
297
277
|
key,
|
|
298
278
|
kind: "function",
|
|
299
279
|
name,
|
|
@@ -339,7 +319,7 @@ function useHookCollector() {
|
|
|
339
319
|
return { ctx, listeners };
|
|
340
320
|
}
|
|
341
321
|
var ERComponentHint = {
|
|
342
|
-
...
|
|
322
|
+
...JSX3__namespace.JSXValueHint,
|
|
343
323
|
// 1n << 0n - 1n << 63n are reserved for JSXValueHint
|
|
344
324
|
// Skip function component created by React.memo
|
|
345
325
|
SkipMemo: 1n << 64n,
|
|
@@ -355,6 +335,17 @@ var ERComponentHint = {
|
|
|
355
335
|
SkipClassProperty: 1n << 69n
|
|
356
336
|
};
|
|
357
337
|
var DEFAULT_COMPONENT_HINT = 0n | ERComponentHint.SkipBooleanLiteral | ERComponentHint.SkipMapCallback | ERComponentHint.SkipNumberLiteral | ERComponentHint.SkipStringLiteral | ERComponentHint.SkipUndefined | ERComponentHint.SkipEmptyArray | ERComponentHint.StrictArray | ERComponentHint.StrictConditional | ERComponentHint.StrictLogical;
|
|
338
|
+
var COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR = [
|
|
339
|
+
"AssignmentExpression",
|
|
340
|
+
"[type]",
|
|
341
|
+
"[operator='=']",
|
|
342
|
+
"[left.type='MemberExpression']",
|
|
343
|
+
"[left.property.name='displayName']"
|
|
344
|
+
].join("");
|
|
345
|
+
function isComponentDisplayNameAssignment(node) {
|
|
346
|
+
if (node == null) return false;
|
|
347
|
+
return node.type === types.AST_NODE_TYPES.AssignmentExpression && node.operator === "=" && node.left.type === types.AST_NODE_TYPES.MemberExpression && node.left.property.type === types.AST_NODE_TYPES.Identifier && node.left.property.name === "displayName";
|
|
348
|
+
}
|
|
358
349
|
|
|
359
350
|
// src/component/component-flag.ts
|
|
360
351
|
var ERClassComponentFlag = {
|
|
@@ -418,6 +409,8 @@ function isPureComponent(node) {
|
|
|
418
409
|
}
|
|
419
410
|
return false;
|
|
420
411
|
}
|
|
412
|
+
|
|
413
|
+
// src/component/component-lifecycle.ts
|
|
421
414
|
function isComponentDidMount(node) {
|
|
422
415
|
return AST8__namespace.isMethodOrProperty(node) && node.key.type === types.AST_NODE_TYPES.Identifier && node.key.name === "componentDidMount";
|
|
423
416
|
}
|
|
@@ -460,9 +453,9 @@ function isComponentName(name) {
|
|
|
460
453
|
return RE_COMPONENT_NAME.test(name);
|
|
461
454
|
}
|
|
462
455
|
function hasNoneOrValidComponentName(node, context) {
|
|
463
|
-
const
|
|
464
|
-
if (
|
|
465
|
-
const name = Array.isArray(
|
|
456
|
+
const id = getFunctionComponentIdentifier(node, context);
|
|
457
|
+
if (id == null) return true;
|
|
458
|
+
const name = Array.isArray(id) ? id.at(-1)?.name : id.name;
|
|
466
459
|
return name != null && isComponentName(name);
|
|
467
460
|
}
|
|
468
461
|
|
|
@@ -505,7 +498,6 @@ function getComponentFlag(initPath) {
|
|
|
505
498
|
}
|
|
506
499
|
return flag;
|
|
507
500
|
}
|
|
508
|
-
var displayNameAssignmentSelector = "AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']";
|
|
509
501
|
function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options = {}) {
|
|
510
502
|
const { collectDisplayName = false, collectHookCalls = false } = options;
|
|
511
503
|
const jsxCtx = { getScope: (node) => context.sourceCode.getScope(node) };
|
|
@@ -513,7 +505,7 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
513
505
|
const functionEntries = [];
|
|
514
506
|
const getCurrentEntry = () => functionEntries.at(-1);
|
|
515
507
|
const onFunctionEnter = (node) => {
|
|
516
|
-
const key = getId();
|
|
508
|
+
const key = shared.getId();
|
|
517
509
|
functionEntries.push({ key, node, hookCalls: [], isComponent: false });
|
|
518
510
|
};
|
|
519
511
|
const onFunctionExit = () => {
|
|
@@ -523,7 +515,7 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
523
515
|
return functionEntries.pop();
|
|
524
516
|
}
|
|
525
517
|
const shouldDrop = AST8__namespace.getNestedReturnStatements(entry.node.body).slice().reverse().some((r) => {
|
|
526
|
-
return context.sourceCode.getScope(r).block === entry.node && r.argument != null && !
|
|
518
|
+
return context.sourceCode.getScope(r).block === entry.node && r.argument != null && !JSX3__namespace.isJSXValue(r.argument, jsxCtx, hint);
|
|
527
519
|
});
|
|
528
520
|
if (shouldDrop) {
|
|
529
521
|
components.delete(entry.key);
|
|
@@ -549,16 +541,16 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
549
541
|
return;
|
|
550
542
|
}
|
|
551
543
|
const { body } = entry.node;
|
|
552
|
-
const isComponent = hasNoneOrValidComponentName(entry.node, context) &&
|
|
544
|
+
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX3__namespace.isJSXValue(body, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
553
545
|
if (!isComponent) {
|
|
554
546
|
return;
|
|
555
547
|
}
|
|
556
548
|
const initPath = AST8__namespace.getFunctionInitPath(entry.node);
|
|
557
|
-
const
|
|
558
|
-
const name = getComponentNameFromIdentifier(
|
|
559
|
-
const key = getId();
|
|
549
|
+
const id = getFunctionComponentIdentifier(entry.node, context);
|
|
550
|
+
const name = getComponentNameFromIdentifier(id);
|
|
551
|
+
const key = shared.getId();
|
|
560
552
|
components.set(key, {
|
|
561
|
-
id
|
|
553
|
+
id,
|
|
562
554
|
key,
|
|
563
555
|
kind: "function",
|
|
564
556
|
name,
|
|
@@ -571,8 +563,9 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
571
563
|
});
|
|
572
564
|
},
|
|
573
565
|
...collectDisplayName ? {
|
|
574
|
-
[
|
|
566
|
+
[COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR](node) {
|
|
575
567
|
const { left, right } = node;
|
|
568
|
+
if (left.type !== types.AST_NODE_TYPES.MemberExpression) return;
|
|
576
569
|
const componentName = left.object.type === types.AST_NODE_TYPES.Identifier ? left.object.name : eff._;
|
|
577
570
|
const component = [...components.values()].findLast(({ name }) => name != null && name === componentName);
|
|
578
571
|
if (component == null) {
|
|
@@ -598,17 +591,17 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
598
591
|
if (entry == null) {
|
|
599
592
|
return;
|
|
600
593
|
}
|
|
601
|
-
const isComponent = hasNoneOrValidComponentName(entry.node, context) &&
|
|
594
|
+
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX3__namespace.isJSXValue(node.argument, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
602
595
|
if (!isComponent) {
|
|
603
596
|
return;
|
|
604
597
|
}
|
|
605
598
|
functionEntries.pop();
|
|
606
599
|
functionEntries.push({ ...entry, isComponent });
|
|
607
600
|
const initPath = AST8__namespace.getFunctionInitPath(entry.node);
|
|
608
|
-
const
|
|
609
|
-
const name = getComponentNameFromIdentifier(
|
|
601
|
+
const id = getFunctionComponentIdentifier(entry.node, context);
|
|
602
|
+
const name = getComponentNameFromIdentifier(id);
|
|
610
603
|
components.set(entry.key, {
|
|
611
|
-
id
|
|
604
|
+
id,
|
|
612
605
|
key: entry.key,
|
|
613
606
|
kind: "function",
|
|
614
607
|
name,
|
|
@@ -635,16 +628,16 @@ function useComponentCollectorLegacy() {
|
|
|
635
628
|
if (!isClassComponent(node)) {
|
|
636
629
|
return;
|
|
637
630
|
}
|
|
638
|
-
const
|
|
639
|
-
const key = getId();
|
|
631
|
+
const id = AST8__namespace.getClassIdentifier(node);
|
|
632
|
+
const key = shared.getId();
|
|
640
633
|
const flag = isPureComponent(node) ? ERClassComponentFlag.PureComponent : ERClassComponentFlag.None;
|
|
641
634
|
components.set(
|
|
642
635
|
key,
|
|
643
636
|
{
|
|
644
|
-
id
|
|
637
|
+
id,
|
|
645
638
|
key,
|
|
646
639
|
kind: "class",
|
|
647
|
-
name:
|
|
640
|
+
name: id?.name,
|
|
648
641
|
node,
|
|
649
642
|
// TODO: Get displayName of class component
|
|
650
643
|
displayName: eff._,
|
|
@@ -671,12 +664,12 @@ function isRenderFunctionLoose(node, context) {
|
|
|
671
664
|
if (AST8__namespace.getFunctionIdentifier(node)?.name.startsWith("render")) {
|
|
672
665
|
return parent.type === types.AST_NODE_TYPES.JSXExpressionContainer && parent.parent.type === types.AST_NODE_TYPES.JSXAttribute && parent.parent.name.type === types.AST_NODE_TYPES.JSXIdentifier && parent.parent.name.name.startsWith("render");
|
|
673
666
|
}
|
|
674
|
-
return
|
|
667
|
+
return JSX3__namespace.isJSXValue(
|
|
675
668
|
body,
|
|
676
669
|
{
|
|
677
670
|
getScope: (node2) => context.sourceCode.getScope(node2)
|
|
678
671
|
},
|
|
679
|
-
|
|
672
|
+
JSX3__namespace.JSXValueHint.SkipNullLiteral | JSX3__namespace.JSXValueHint.SkipUndefined | JSX3__namespace.JSXValueHint.StrictLogical | JSX3__namespace.JSXValueHint.StrictConditional
|
|
680
673
|
);
|
|
681
674
|
}
|
|
682
675
|
function isRenderPropLoose(node, context) {
|
|
@@ -716,6 +709,7 @@ function isFunctionOfUseEffectCleanup(node) {
|
|
|
716
709
|
return nearFunction === nearFunctionOfReturn && isFunctionOfUseEffectSetup(nearFunction);
|
|
717
710
|
}
|
|
718
711
|
|
|
712
|
+
exports.COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR = COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR;
|
|
719
713
|
exports.DEFAULT_COMPONENT_HINT = DEFAULT_COMPONENT_HINT;
|
|
720
714
|
exports.ERClassComponentFlag = ERClassComponentFlag;
|
|
721
715
|
exports.ERComponentHint = ERComponentHint;
|
|
@@ -724,9 +718,7 @@ exports.ERPhaseRelevance = ERPhaseRelevance;
|
|
|
724
718
|
exports.RE_COMPONENT_NAME = RE_COMPONENT_NAME;
|
|
725
719
|
exports.RE_HOOK_NAME = RE_HOOK_NAME;
|
|
726
720
|
exports.getComponentNameFromIdentifier = getComponentNameFromIdentifier;
|
|
727
|
-
exports.getElementNameOnJsxAndHtml = getElementNameOnJsxAndHtml;
|
|
728
721
|
exports.getFunctionComponentIdentifier = getFunctionComponentIdentifier;
|
|
729
|
-
exports.getId = getId;
|
|
730
722
|
exports.hasNoneOrValidComponentName = hasNoneOrValidComponentName;
|
|
731
723
|
exports.isCallFromReact = isCallFromReact;
|
|
732
724
|
exports.isCallFromReactMember = isCallFromReactMember;
|
|
@@ -746,6 +738,7 @@ exports.isCloneElement = isCloneElement;
|
|
|
746
738
|
exports.isCloneElementCall = isCloneElementCall;
|
|
747
739
|
exports.isComponentDidCatch = isComponentDidCatch;
|
|
748
740
|
exports.isComponentDidMount = isComponentDidMount;
|
|
741
|
+
exports.isComponentDisplayNameAssignment = isComponentDisplayNameAssignment;
|
|
749
742
|
exports.isComponentName = isComponentName;
|
|
750
743
|
exports.isComponentWillUnmount = isComponentWillUnmount;
|
|
751
744
|
exports.isCreateContext = isCreateContext;
|
package/dist/index.mjs
CHANGED
|
@@ -1,37 +1,13 @@
|
|
|
1
1
|
import * as AST8 from '@eslint-react/ast';
|
|
2
2
|
import { flip, dual, _, returnFalse } from '@eslint-react/eff';
|
|
3
|
-
import * as
|
|
3
|
+
import * as JSX3 from '@eslint-react/jsx';
|
|
4
|
+
import { unsafeDecodeSettings, DEFAULT_ESLINT_REACT_SETTINGS, getId } from '@eslint-react/shared';
|
|
4
5
|
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
5
6
|
import * as VAR from '@eslint-react/var';
|
|
6
|
-
import { unsafeDecodeSettings } from '@eslint-react/shared';
|
|
7
|
-
import 'ts-pattern';
|
|
8
7
|
import birecord from 'birecord';
|
|
9
8
|
|
|
10
9
|
// src/component/component-collector.ts
|
|
11
|
-
function
|
|
12
|
-
const name = JSX4.getElementName(node);
|
|
13
|
-
if (name === name.toLowerCase()) return [name, name];
|
|
14
|
-
const component = additionalComponents.findLast((c) => c.name === name || c.re.test(name));
|
|
15
|
-
if (component != null) return [name, component.as];
|
|
16
|
-
if (polymorphicPropName == null) return [name, name];
|
|
17
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
18
|
-
const attributeNode = JSX4.getAttributeNode(
|
|
19
|
-
polymorphicPropName,
|
|
20
|
-
initialScope,
|
|
21
|
-
node.attributes
|
|
22
|
-
);
|
|
23
|
-
if (attributeNode == null) return [name, name];
|
|
24
|
-
const polymorphicPropValue = VAR.toResolved(JSX4.getAttributeStaticValue(attributeNode, initialScope)).value;
|
|
25
|
-
if (typeof polymorphicPropValue === "string") {
|
|
26
|
-
return [name, polymorphicPropValue];
|
|
27
|
-
}
|
|
28
|
-
return [name, name];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/utils/get-id.ts
|
|
32
|
-
var id = 0n;
|
|
33
|
-
var getId = () => (id++).toString();
|
|
34
|
-
function isInitializedFromReact(name, initialScope, importSource = "react") {
|
|
10
|
+
function isInitializedFromReact(name, importSource, initialScope) {
|
|
35
11
|
return name.toLowerCase() === "react" || VAR.isInitializedFromSource(
|
|
36
12
|
name,
|
|
37
13
|
importSource,
|
|
@@ -49,12 +25,13 @@ function isFromReact(name) {
|
|
|
49
25
|
}
|
|
50
26
|
return node.name === name;
|
|
51
27
|
}
|
|
28
|
+
const importSource = settings.importSource ?? DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
52
29
|
const initialScope = context.sourceCode.getScope(node);
|
|
53
30
|
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
54
|
-
return node.object.type === AST_NODE_TYPES.Identifier && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === name && isInitializedFromReact(node.object.name,
|
|
31
|
+
return node.object.type === AST_NODE_TYPES.Identifier && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === name && isInitializedFromReact(node.object.name, importSource, initialScope);
|
|
55
32
|
}
|
|
56
33
|
if (node.name === name) {
|
|
57
|
-
return isInitializedFromReact(name,
|
|
34
|
+
return isInitializedFromReact(name, importSource, initialScope);
|
|
58
35
|
}
|
|
59
36
|
return false;
|
|
60
37
|
};
|
|
@@ -74,14 +51,15 @@ function isFromReactMember(memberName, name) {
|
|
|
74
51
|
}
|
|
75
52
|
return false;
|
|
76
53
|
}
|
|
54
|
+
const importSource = settings.importSource ?? DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
77
55
|
const initialScope = context.sourceCode.getScope(node);
|
|
78
56
|
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name) {
|
|
79
57
|
return false;
|
|
80
58
|
}
|
|
81
59
|
if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === memberName) {
|
|
82
|
-
return isInitializedFromReact(node.object.name,
|
|
60
|
+
return isInitializedFromReact(node.object.name, importSource, initialScope);
|
|
83
61
|
}
|
|
84
|
-
if (node.object.type === AST_NODE_TYPES.MemberExpression && node.object.object.type === AST_NODE_TYPES.Identifier && isInitializedFromReact(node.object.object.name,
|
|
62
|
+
if (node.object.type === AST_NODE_TYPES.MemberExpression && node.object.object.type === AST_NODE_TYPES.Identifier && isInitializedFromReact(node.object.object.name, importSource, initialScope) && node.object.property.type === AST_NODE_TYPES.Identifier) {
|
|
85
63
|
return node.object.property.name === memberName;
|
|
86
64
|
}
|
|
87
65
|
return false;
|
|
@@ -164,8 +142,8 @@ function isReactHookName(name) {
|
|
|
164
142
|
// src/hook/is.ts
|
|
165
143
|
function isReactHook(node) {
|
|
166
144
|
if (node == null) return _;
|
|
167
|
-
const
|
|
168
|
-
return
|
|
145
|
+
const id = AST8.getFunctionIdentifier(node);
|
|
146
|
+
return id?.name != null && isReactHookName(id.name);
|
|
169
147
|
}
|
|
170
148
|
function isReactHookCall(node) {
|
|
171
149
|
if (node == null) return false;
|
|
@@ -183,13 +161,14 @@ function isReactHookCall(node) {
|
|
|
183
161
|
function isReactHookCallWithName(node, context) {
|
|
184
162
|
if (node == null) return returnFalse;
|
|
185
163
|
const settings = unsafeDecodeSettings(context.settings);
|
|
164
|
+
const importSource = settings.importSource ?? "react";
|
|
165
|
+
const initialScope = context.sourceCode.getScope(node);
|
|
186
166
|
return (name) => {
|
|
187
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
188
167
|
switch (true) {
|
|
189
168
|
case (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
190
|
-
return !settings.strictImportCheck || isInitializedFromReact(name,
|
|
169
|
+
return !settings.strictImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
191
170
|
case (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
192
|
-
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name,
|
|
171
|
+
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
193
172
|
default:
|
|
194
173
|
return false;
|
|
195
174
|
}
|
|
@@ -210,13 +189,14 @@ function isReactHookCallWithNameLoose(node) {
|
|
|
210
189
|
}
|
|
211
190
|
function isReactHookCallWithNameAlias(name, context, alias) {
|
|
212
191
|
const settings = unsafeDecodeSettings(context);
|
|
192
|
+
const importSource = settings.importSource ?? "react";
|
|
213
193
|
return (node) => {
|
|
214
194
|
const initialScope = context.sourceCode.getScope(node);
|
|
215
195
|
switch (true) {
|
|
216
196
|
case (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
217
|
-
return !settings.strictImportCheck || isInitializedFromReact(name,
|
|
197
|
+
return !settings.strictImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
218
198
|
case (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name && "name" in node.callee.object):
|
|
219
|
-
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name,
|
|
199
|
+
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
220
200
|
default:
|
|
221
201
|
return alias.some(isReactHookCallWithNameLoose(node));
|
|
222
202
|
}
|
|
@@ -260,13 +240,13 @@ function useHookCollector() {
|
|
|
260
240
|
const hooks = /* @__PURE__ */ new Map();
|
|
261
241
|
const fEntries = [];
|
|
262
242
|
const onFunctionEnter = (node) => {
|
|
263
|
-
const
|
|
264
|
-
const name =
|
|
243
|
+
const id = AST8.getFunctionIdentifier(node);
|
|
244
|
+
const name = id?.name;
|
|
265
245
|
if (name != null && isReactHookName(name)) {
|
|
266
246
|
const key = getId();
|
|
267
247
|
fEntries.push({ key, node });
|
|
268
248
|
hooks.set(key, {
|
|
269
|
-
id
|
|
249
|
+
id,
|
|
270
250
|
key,
|
|
271
251
|
kind: "function",
|
|
272
252
|
name,
|
|
@@ -312,7 +292,7 @@ function useHookCollector() {
|
|
|
312
292
|
return { ctx, listeners };
|
|
313
293
|
}
|
|
314
294
|
var ERComponentHint = {
|
|
315
|
-
...
|
|
295
|
+
...JSX3.JSXValueHint,
|
|
316
296
|
// 1n << 0n - 1n << 63n are reserved for JSXValueHint
|
|
317
297
|
// Skip function component created by React.memo
|
|
318
298
|
SkipMemo: 1n << 64n,
|
|
@@ -328,6 +308,17 @@ var ERComponentHint = {
|
|
|
328
308
|
SkipClassProperty: 1n << 69n
|
|
329
309
|
};
|
|
330
310
|
var DEFAULT_COMPONENT_HINT = 0n | ERComponentHint.SkipBooleanLiteral | ERComponentHint.SkipMapCallback | ERComponentHint.SkipNumberLiteral | ERComponentHint.SkipStringLiteral | ERComponentHint.SkipUndefined | ERComponentHint.SkipEmptyArray | ERComponentHint.StrictArray | ERComponentHint.StrictConditional | ERComponentHint.StrictLogical;
|
|
311
|
+
var COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR = [
|
|
312
|
+
"AssignmentExpression",
|
|
313
|
+
"[type]",
|
|
314
|
+
"[operator='=']",
|
|
315
|
+
"[left.type='MemberExpression']",
|
|
316
|
+
"[left.property.name='displayName']"
|
|
317
|
+
].join("");
|
|
318
|
+
function isComponentDisplayNameAssignment(node) {
|
|
319
|
+
if (node == null) return false;
|
|
320
|
+
return node.type === AST_NODE_TYPES.AssignmentExpression && node.operator === "=" && node.left.type === AST_NODE_TYPES.MemberExpression && node.left.property.type === AST_NODE_TYPES.Identifier && node.left.property.name === "displayName";
|
|
321
|
+
}
|
|
331
322
|
|
|
332
323
|
// src/component/component-flag.ts
|
|
333
324
|
var ERClassComponentFlag = {
|
|
@@ -391,6 +382,8 @@ function isPureComponent(node) {
|
|
|
391
382
|
}
|
|
392
383
|
return false;
|
|
393
384
|
}
|
|
385
|
+
|
|
386
|
+
// src/component/component-lifecycle.ts
|
|
394
387
|
function isComponentDidMount(node) {
|
|
395
388
|
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidMount";
|
|
396
389
|
}
|
|
@@ -433,9 +426,9 @@ function isComponentName(name) {
|
|
|
433
426
|
return RE_COMPONENT_NAME.test(name);
|
|
434
427
|
}
|
|
435
428
|
function hasNoneOrValidComponentName(node, context) {
|
|
436
|
-
const
|
|
437
|
-
if (
|
|
438
|
-
const name = Array.isArray(
|
|
429
|
+
const id = getFunctionComponentIdentifier(node, context);
|
|
430
|
+
if (id == null) return true;
|
|
431
|
+
const name = Array.isArray(id) ? id.at(-1)?.name : id.name;
|
|
439
432
|
return name != null && isComponentName(name);
|
|
440
433
|
}
|
|
441
434
|
|
|
@@ -478,7 +471,6 @@ function getComponentFlag(initPath) {
|
|
|
478
471
|
}
|
|
479
472
|
return flag;
|
|
480
473
|
}
|
|
481
|
-
var displayNameAssignmentSelector = "AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']";
|
|
482
474
|
function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options = {}) {
|
|
483
475
|
const { collectDisplayName = false, collectHookCalls = false } = options;
|
|
484
476
|
const jsxCtx = { getScope: (node) => context.sourceCode.getScope(node) };
|
|
@@ -496,7 +488,7 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
496
488
|
return functionEntries.pop();
|
|
497
489
|
}
|
|
498
490
|
const shouldDrop = AST8.getNestedReturnStatements(entry.node.body).slice().reverse().some((r) => {
|
|
499
|
-
return context.sourceCode.getScope(r).block === entry.node && r.argument != null && !
|
|
491
|
+
return context.sourceCode.getScope(r).block === entry.node && r.argument != null && !JSX3.isJSXValue(r.argument, jsxCtx, hint);
|
|
500
492
|
});
|
|
501
493
|
if (shouldDrop) {
|
|
502
494
|
components.delete(entry.key);
|
|
@@ -522,16 +514,16 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
522
514
|
return;
|
|
523
515
|
}
|
|
524
516
|
const { body } = entry.node;
|
|
525
|
-
const isComponent = hasNoneOrValidComponentName(entry.node, context) &&
|
|
517
|
+
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX3.isJSXValue(body, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
526
518
|
if (!isComponent) {
|
|
527
519
|
return;
|
|
528
520
|
}
|
|
529
521
|
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
530
|
-
const
|
|
531
|
-
const name = getComponentNameFromIdentifier(
|
|
522
|
+
const id = getFunctionComponentIdentifier(entry.node, context);
|
|
523
|
+
const name = getComponentNameFromIdentifier(id);
|
|
532
524
|
const key = getId();
|
|
533
525
|
components.set(key, {
|
|
534
|
-
id
|
|
526
|
+
id,
|
|
535
527
|
key,
|
|
536
528
|
kind: "function",
|
|
537
529
|
name,
|
|
@@ -544,8 +536,9 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
544
536
|
});
|
|
545
537
|
},
|
|
546
538
|
...collectDisplayName ? {
|
|
547
|
-
[
|
|
539
|
+
[COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR](node) {
|
|
548
540
|
const { left, right } = node;
|
|
541
|
+
if (left.type !== AST_NODE_TYPES.MemberExpression) return;
|
|
549
542
|
const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : _;
|
|
550
543
|
const component = [...components.values()].findLast(({ name }) => name != null && name === componentName);
|
|
551
544
|
if (component == null) {
|
|
@@ -571,17 +564,17 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
571
564
|
if (entry == null) {
|
|
572
565
|
return;
|
|
573
566
|
}
|
|
574
|
-
const isComponent = hasNoneOrValidComponentName(entry.node, context) &&
|
|
567
|
+
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX3.isJSXValue(node.argument, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
575
568
|
if (!isComponent) {
|
|
576
569
|
return;
|
|
577
570
|
}
|
|
578
571
|
functionEntries.pop();
|
|
579
572
|
functionEntries.push({ ...entry, isComponent });
|
|
580
573
|
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
581
|
-
const
|
|
582
|
-
const name = getComponentNameFromIdentifier(
|
|
574
|
+
const id = getFunctionComponentIdentifier(entry.node, context);
|
|
575
|
+
const name = getComponentNameFromIdentifier(id);
|
|
583
576
|
components.set(entry.key, {
|
|
584
|
-
id
|
|
577
|
+
id,
|
|
585
578
|
key: entry.key,
|
|
586
579
|
kind: "function",
|
|
587
580
|
name,
|
|
@@ -608,16 +601,16 @@ function useComponentCollectorLegacy() {
|
|
|
608
601
|
if (!isClassComponent(node)) {
|
|
609
602
|
return;
|
|
610
603
|
}
|
|
611
|
-
const
|
|
604
|
+
const id = AST8.getClassIdentifier(node);
|
|
612
605
|
const key = getId();
|
|
613
606
|
const flag = isPureComponent(node) ? ERClassComponentFlag.PureComponent : ERClassComponentFlag.None;
|
|
614
607
|
components.set(
|
|
615
608
|
key,
|
|
616
609
|
{
|
|
617
|
-
id
|
|
610
|
+
id,
|
|
618
611
|
key,
|
|
619
612
|
kind: "class",
|
|
620
|
-
name:
|
|
613
|
+
name: id?.name,
|
|
621
614
|
node,
|
|
622
615
|
// TODO: Get displayName of class component
|
|
623
616
|
displayName: _,
|
|
@@ -644,12 +637,12 @@ function isRenderFunctionLoose(node, context) {
|
|
|
644
637
|
if (AST8.getFunctionIdentifier(node)?.name.startsWith("render")) {
|
|
645
638
|
return parent.type === AST_NODE_TYPES.JSXExpressionContainer && parent.parent.type === AST_NODE_TYPES.JSXAttribute && parent.parent.name.type === AST_NODE_TYPES.JSXIdentifier && parent.parent.name.name.startsWith("render");
|
|
646
639
|
}
|
|
647
|
-
return
|
|
640
|
+
return JSX3.isJSXValue(
|
|
648
641
|
body,
|
|
649
642
|
{
|
|
650
643
|
getScope: (node2) => context.sourceCode.getScope(node2)
|
|
651
644
|
},
|
|
652
|
-
|
|
645
|
+
JSX3.JSXValueHint.SkipNullLiteral | JSX3.JSXValueHint.SkipUndefined | JSX3.JSXValueHint.StrictLogical | JSX3.JSXValueHint.StrictConditional
|
|
653
646
|
);
|
|
654
647
|
}
|
|
655
648
|
function isRenderPropLoose(node, context) {
|
|
@@ -689,4 +682,4 @@ function isFunctionOfUseEffectCleanup(node) {
|
|
|
689
682
|
return nearFunction === nearFunctionOfReturn && isFunctionOfUseEffectSetup(nearFunction);
|
|
690
683
|
}
|
|
691
684
|
|
|
692
|
-
export { DEFAULT_COMPONENT_HINT, ERClassComponentFlag, ERComponentHint, ERFunctionComponentFlag, ERPhaseRelevance, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier,
|
|
685
|
+
export { COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR, DEFAULT_COMPONENT_HINT, ERClassComponentFlag, ERComponentHint, ERFunctionComponentFlag, ERPhaseRelevance, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier, getFunctionComponentIdentifier, hasNoneOrValidComponentName, isCallFromReact, isCallFromReactMember, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDisplayNameAssignment, isComponentName, isComponentWillUnmount, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFromReact, isFromReactMember, isFunctionOfComponentDidMount, isFunctionOfComponentWillUnmount, isFunctionOfRenderMethod, isFunctionOfUseEffectCleanup, isFunctionOfUseEffectSetup, isGetDerivedStateFromError, isGetDerivedStateFromProps, isInitializedFromReact, isInsideCreateElementProps, isInsideReactHook, isInsideRenderMethod, isInversePhase, isMemo, isMemoCall, isPureComponent, isReactAPICallWithName, isReactAPIWithName, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookName, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isThisSetState, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCallLoose, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-react/core",
|
|
3
|
-
"version": "1.24.0
|
|
3
|
+
"version": "1.24.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": {
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"@typescript-eslint/types": "^8.20.0",
|
|
39
39
|
"@typescript-eslint/utils": "^8.20.0",
|
|
40
40
|
"birecord": "^0.1.1",
|
|
41
|
-
"ts-pattern": "^5.6.
|
|
42
|
-
"@eslint-react/ast": "1.24.0
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/jsx": "1.24.0
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/
|
|
41
|
+
"ts-pattern": "^5.6.1",
|
|
42
|
+
"@eslint-react/ast": "1.24.0",
|
|
43
|
+
"@eslint-react/var": "1.24.0",
|
|
44
|
+
"@eslint-react/jsx": "1.24.0",
|
|
45
|
+
"@eslint-react/shared": "1.24.0",
|
|
46
|
+
"@eslint-react/eff": "1.24.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"tsup": "^8.3.5",
|