@eslint-react/core 1.28.0-beta.0 → 1.28.0-beta.4
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 +64 -67
- package/dist/index.d.ts +64 -67
- package/dist/index.js +180 -157
- package/dist/index.mjs +176 -158
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as AST8 from '@eslint-react/ast';
|
|
2
2
|
import { flip, dual, constFalse, identity, _ } from '@eslint-react/eff';
|
|
3
3
|
import * as JSX3 from '@eslint-react/jsx';
|
|
4
|
-
import {
|
|
4
|
+
import { DEFAULT_ESLINT_REACT_SETTINGS, unsafeDecodeSettings, getId } from '@eslint-react/shared';
|
|
5
5
|
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
6
6
|
import * as VAR from '@eslint-react/var';
|
|
7
7
|
import { match, P } from 'ts-pattern';
|
|
8
8
|
import birecord from 'birecord';
|
|
9
9
|
|
|
10
10
|
// src/component/component-collector.ts
|
|
11
|
-
var DISPLAY_NAME_ASSIGNMENT_SELECTOR = [
|
|
12
|
-
"AssignmentExpression",
|
|
13
|
-
"[type]",
|
|
14
|
-
"[operator='=']",
|
|
15
|
-
"[left.type='MemberExpression']",
|
|
16
|
-
"[left.property.name='displayName']"
|
|
17
|
-
].join("");
|
|
18
|
-
function isDisplayNameAssignment(node) {
|
|
19
|
-
if (node == null) return false;
|
|
20
|
-
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";
|
|
21
|
-
}
|
|
22
11
|
function getRequireExpressionArguments(node) {
|
|
23
12
|
return match(node).with({ type: AST_NODE_TYPES.CallExpression, arguments: P.select(), callee: { type: AST_NODE_TYPES.Identifier, name: "require" } }, identity).with({ type: AST_NODE_TYPES.MemberExpression, object: P.select() }, getRequireExpressionArguments).otherwise(() => _);
|
|
24
13
|
}
|
|
25
|
-
function isInitializedFromReact(name,
|
|
14
|
+
function isInitializedFromReact(name, importSource, initialScope) {
|
|
26
15
|
if (name.toLowerCase() === "react") return true;
|
|
27
16
|
const latestDef = VAR.findVariable(name, initialScope)?.defs.at(-1);
|
|
28
17
|
if (latestDef == null) return false;
|
|
@@ -30,81 +19,101 @@ function isInitializedFromReact(name, source, initialScope) {
|
|
|
30
19
|
if (node.type === AST_NODE_TYPES.VariableDeclarator && node.init != null) {
|
|
31
20
|
const { init } = node;
|
|
32
21
|
if (init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier) {
|
|
33
|
-
return isInitializedFromReact(init.object.name,
|
|
22
|
+
return isInitializedFromReact(init.object.name, importSource, initialScope);
|
|
34
23
|
}
|
|
35
24
|
if (init.type === AST_NODE_TYPES.Identifier) {
|
|
36
|
-
return isInitializedFromReact(init.name,
|
|
25
|
+
return isInitializedFromReact(init.name, importSource, initialScope);
|
|
37
26
|
}
|
|
38
27
|
const args = getRequireExpressionArguments(init);
|
|
39
28
|
const arg0 = args?.[0];
|
|
40
|
-
if (arg0 == null || !
|
|
29
|
+
if (arg0 == null || !AST8.isKindOfLiteral(arg0, "string")) {
|
|
41
30
|
return false;
|
|
42
31
|
}
|
|
43
|
-
return arg0.value ===
|
|
32
|
+
return arg0.value === importSource || arg0.value.startsWith(`${importSource}/`);
|
|
44
33
|
}
|
|
45
|
-
return parent?.type === AST_NODE_TYPES.ImportDeclaration && parent.source.value ===
|
|
34
|
+
return parent?.type === AST_NODE_TYPES.ImportDeclaration && parent.source.value === importSource;
|
|
46
35
|
}
|
|
47
36
|
|
|
48
37
|
// src/utils/is-from-react.ts
|
|
38
|
+
var defaultImportSource = DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
39
|
+
function isFromReactLoose(node, name) {
|
|
40
|
+
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
41
|
+
return node.object.type === AST_NODE_TYPES.Identifier && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === name;
|
|
42
|
+
}
|
|
43
|
+
return node.name === name;
|
|
44
|
+
}
|
|
45
|
+
function isFromReactStrict(node, name, importSource, initialScope) {
|
|
46
|
+
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
47
|
+
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);
|
|
48
|
+
}
|
|
49
|
+
if (node.name === name) {
|
|
50
|
+
return isInitializedFromReact(name, importSource, initialScope);
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
49
54
|
function isFromReact(name) {
|
|
50
|
-
return (
|
|
51
|
-
const
|
|
52
|
-
if (!
|
|
53
|
-
|
|
54
|
-
return node.object.type === AST_NODE_TYPES.Identifier && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === name;
|
|
55
|
-
}
|
|
56
|
-
return node.name === name;
|
|
57
|
-
}
|
|
58
|
-
const importSource = settings.importSource ?? DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
59
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
60
|
-
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
61
|
-
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);
|
|
62
|
-
}
|
|
63
|
-
if (node.name === name) {
|
|
64
|
-
return isInitializedFromReact(name, importSource, initialScope);
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
55
|
+
return (context, node) => {
|
|
56
|
+
const { importSource = defaultImportSource, strictImportCheck = false } = unsafeDecodeSettings(context.settings);
|
|
57
|
+
if (!strictImportCheck) return isFromReactLoose(node, name);
|
|
58
|
+
return isFromReactStrict(node, name, importSource, context.sourceCode.getScope(node));
|
|
67
59
|
};
|
|
68
60
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
82
|
-
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === memberName) {
|
|
86
|
-
return isInitializedFromReact(node.object.name, importSource, initialScope);
|
|
87
|
-
}
|
|
88
|
-
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) {
|
|
89
|
-
return node.object.property.name === memberName;
|
|
90
|
-
}
|
|
61
|
+
function isFromReactMemberLoose(node, memberName, name) {
|
|
62
|
+
const { object, property } = node;
|
|
63
|
+
if (property.type !== AST_NODE_TYPES.Identifier || property.name !== name) return false;
|
|
64
|
+
if (object.type === AST_NODE_TYPES.Identifier && object.name === memberName) return true;
|
|
65
|
+
if (object.type === AST_NODE_TYPES.MemberExpression && object.object.type === AST_NODE_TYPES.Identifier && object.property.type === AST_NODE_TYPES.Identifier) {
|
|
66
|
+
return object.property.name === memberName;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
function isFromReactMemberStrict(node, memberName, name, importSource, initialScope) {
|
|
71
|
+
const { object, property } = node;
|
|
72
|
+
if (property.type !== AST_NODE_TYPES.Identifier || property.name !== name) {
|
|
91
73
|
return false;
|
|
74
|
+
}
|
|
75
|
+
if (object.type === AST_NODE_TYPES.Identifier && object.name === memberName) {
|
|
76
|
+
return isInitializedFromReact(object.name, importSource, initialScope);
|
|
77
|
+
}
|
|
78
|
+
if (object.type === AST_NODE_TYPES.MemberExpression && object.object.type === AST_NODE_TYPES.Identifier && isInitializedFromReact(object.object.name, importSource, initialScope) && object.property.type === AST_NODE_TYPES.Identifier) {
|
|
79
|
+
return object.property.name === memberName;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function isFromReactMember(memberName, name) {
|
|
84
|
+
return (context, node) => {
|
|
85
|
+
const { importSource = defaultImportSource, strictImportCheck = false } = unsafeDecodeSettings(context.settings);
|
|
86
|
+
if (!strictImportCheck) return isFromReactMemberLoose(node, memberName, name);
|
|
87
|
+
return isFromReactMemberStrict(node, memberName, name, importSource, context.sourceCode.getScope(node));
|
|
92
88
|
};
|
|
93
89
|
}
|
|
90
|
+
|
|
91
|
+
// src/utils/is-call-from-react.ts
|
|
94
92
|
function isCallFromReact(name) {
|
|
95
|
-
return dual(2, (
|
|
93
|
+
return dual(2, (context, node) => {
|
|
96
94
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
97
|
-
if (!
|
|
98
|
-
return isFromReact(name)(node.callee
|
|
95
|
+
if (!AST8.isOneOf([AST_NODE_TYPES.Identifier, AST_NODE_TYPES.MemberExpression])(node.callee)) return false;
|
|
96
|
+
return isFromReact(name)(context, node.callee);
|
|
99
97
|
});
|
|
100
98
|
}
|
|
101
99
|
function isCallFromReactMember(pragmaMemberName, name) {
|
|
102
|
-
return dual(2, (
|
|
100
|
+
return dual(2, (context, node) => {
|
|
103
101
|
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
104
|
-
if (!
|
|
105
|
-
return isFromReactMember(pragmaMemberName, name)(node.callee
|
|
102
|
+
if (!AST8.is(AST_NODE_TYPES.MemberExpression)(node.callee)) return false;
|
|
103
|
+
return isFromReactMember(pragmaMemberName, name)(context, node.callee);
|
|
106
104
|
});
|
|
107
105
|
}
|
|
106
|
+
var DISPLAY_NAME_ASSIGNMENT_SELECTOR = [
|
|
107
|
+
"AssignmentExpression",
|
|
108
|
+
"[type]",
|
|
109
|
+
"[operator='=']",
|
|
110
|
+
"[left.type='MemberExpression']",
|
|
111
|
+
"[left.property.name='displayName']"
|
|
112
|
+
].join("");
|
|
113
|
+
function isDisplayNameAssignment(node) {
|
|
114
|
+
if (node == null) return false;
|
|
115
|
+
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";
|
|
116
|
+
}
|
|
108
117
|
|
|
109
118
|
// src/utils/is-react-api.ts
|
|
110
119
|
function isReactAPIWithName(name, member) {
|
|
@@ -136,21 +145,6 @@ var isCreateRefCall = isReactAPICallWithName("createRef");
|
|
|
136
145
|
var isForwardRefCall = isReactAPICallWithName("forwardRef");
|
|
137
146
|
var isMemoCall = isReactAPICallWithName("memo");
|
|
138
147
|
|
|
139
|
-
// src/element/hierarchy.ts
|
|
140
|
-
function isInsideCreateElementProps(node, context) {
|
|
141
|
-
const call = AST9.findParentNode(node, isCreateElementCall(context));
|
|
142
|
-
if (call == null) return false;
|
|
143
|
-
const prop = AST9.findParentNode(node, AST9.is(AST_NODE_TYPES.ObjectExpression));
|
|
144
|
-
if (prop == null) return false;
|
|
145
|
-
return prop === call.arguments[1];
|
|
146
|
-
}
|
|
147
|
-
function isChildrenOfCreateElement(node, context) {
|
|
148
|
-
const parent = node.parent;
|
|
149
|
-
if (parent == null || parent.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
150
|
-
if (!isCreateElementCall(parent, context)) return false;
|
|
151
|
-
return parent.arguments.slice(2).some((arg) => arg === node);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
148
|
// src/hook/hook-name.ts
|
|
155
149
|
var RE_HOOK_NAME = /^use[A-Z\d]/u;
|
|
156
150
|
function isReactHookName(name) {
|
|
@@ -160,7 +154,7 @@ function isReactHookName(name) {
|
|
|
160
154
|
// src/hook/is.ts
|
|
161
155
|
function isReactHook(node) {
|
|
162
156
|
if (node == null) return _;
|
|
163
|
-
const id =
|
|
157
|
+
const id = AST8.getFunctionIdentifier(node);
|
|
164
158
|
return id?.name != null && isReactHookName(id.name);
|
|
165
159
|
}
|
|
166
160
|
function isReactHookCall(node) {
|
|
@@ -176,7 +170,7 @@ function isReactHookCall(node) {
|
|
|
176
170
|
}
|
|
177
171
|
return false;
|
|
178
172
|
}
|
|
179
|
-
function isReactHookCallWithName(
|
|
173
|
+
function isReactHookCallWithName(context, node) {
|
|
180
174
|
if (node == null) return constFalse;
|
|
181
175
|
const settings = unsafeDecodeSettings(context.settings);
|
|
182
176
|
const importSource = settings.importSource ?? "react";
|
|
@@ -205,7 +199,7 @@ function isReactHookCallWithNameLoose(node) {
|
|
|
205
199
|
}
|
|
206
200
|
};
|
|
207
201
|
}
|
|
208
|
-
function isReactHookCallWithNameAlias(
|
|
202
|
+
function isReactHookCallWithNameAlias(context, name, alias) {
|
|
209
203
|
const settings = unsafeDecodeSettings(context);
|
|
210
204
|
const importSource = settings.importSource ?? "react";
|
|
211
205
|
return (node) => {
|
|
@@ -252,13 +246,13 @@ var isUseTransitionCall = flip(isReactHookCallWithName)("useTransition");
|
|
|
252
246
|
|
|
253
247
|
// src/hook/hierarchy.ts
|
|
254
248
|
function isInsideReactHook(node) {
|
|
255
|
-
return isReactHook(
|
|
249
|
+
return isReactHook(AST8.findParentNode(node, AST8.isFunction));
|
|
256
250
|
}
|
|
257
251
|
function useHookCollector() {
|
|
258
252
|
const hooks = /* @__PURE__ */ new Map();
|
|
259
253
|
const functionEntries = [];
|
|
260
254
|
const onFunctionEnter = (node) => {
|
|
261
|
-
const id =
|
|
255
|
+
const id = AST8.getFunctionIdentifier(node);
|
|
262
256
|
const key = getId();
|
|
263
257
|
const name = id?.name;
|
|
264
258
|
if (name != null && isReactHookName(name)) {
|
|
@@ -347,26 +341,53 @@ var ERComponentFlag = {
|
|
|
347
341
|
ForwardRef: 1n << 3n,
|
|
348
342
|
Async: 1n << 4n
|
|
349
343
|
};
|
|
350
|
-
function isComponentWrapperCall(
|
|
344
|
+
function isComponentWrapperCall(context, node) {
|
|
351
345
|
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
352
346
|
return false;
|
|
353
347
|
}
|
|
354
|
-
return isMemoCall(
|
|
348
|
+
return isMemoCall(context, node) || isForwardRefCall(context, node) || isReactHookCallWithNameLoose(node)("useCallback");
|
|
355
349
|
}
|
|
356
|
-
function getFunctionComponentIdentifier(
|
|
357
|
-
const functionId =
|
|
350
|
+
function getFunctionComponentIdentifier(context, node) {
|
|
351
|
+
const functionId = AST8.getFunctionIdentifier(node);
|
|
358
352
|
if (functionId != null) {
|
|
359
353
|
return functionId;
|
|
360
354
|
}
|
|
361
355
|
const { parent } = node;
|
|
362
|
-
if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(
|
|
356
|
+
if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(context, parent) && parent.parent.type === AST_NODE_TYPES.VariableDeclarator && parent.parent.id.type === AST_NODE_TYPES.Identifier) {
|
|
363
357
|
return parent.parent.id;
|
|
364
358
|
}
|
|
365
|
-
if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(
|
|
359
|
+
if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(context, parent) && parent.parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(context, parent.parent) && parent.parent.parent.type === AST_NODE_TYPES.VariableDeclarator && parent.parent.parent.id.type === AST_NODE_TYPES.Identifier) {
|
|
366
360
|
return parent.parent.parent.id;
|
|
367
361
|
}
|
|
368
362
|
return _;
|
|
369
363
|
}
|
|
364
|
+
var RE_COMPONENT_NAME = /^_?[A-Z]/u;
|
|
365
|
+
function getComponentNameFromIdentifier(node) {
|
|
366
|
+
if (node == null) return _;
|
|
367
|
+
return Array.isArray(node) ? node.map((n) => n.name).join(".") : node.name;
|
|
368
|
+
}
|
|
369
|
+
function isComponentName(name) {
|
|
370
|
+
return RE_COMPONENT_NAME.test(name);
|
|
371
|
+
}
|
|
372
|
+
function hasNoneOrValidComponentName(context, node) {
|
|
373
|
+
const id = getFunctionComponentIdentifier(context, node);
|
|
374
|
+
if (id == null) return true;
|
|
375
|
+
const name = Array.isArray(id) ? id.at(-1)?.name : id.name;
|
|
376
|
+
return name != null && isComponentName(name);
|
|
377
|
+
}
|
|
378
|
+
function isInsideCreateElementProps(context, node) {
|
|
379
|
+
const call = AST8.findParentNode(node, isCreateElementCall(context));
|
|
380
|
+
if (call == null) return false;
|
|
381
|
+
const prop = AST8.findParentNode(node, AST8.is(AST_NODE_TYPES.ObjectExpression));
|
|
382
|
+
if (prop == null) return false;
|
|
383
|
+
return prop === call.arguments[1];
|
|
384
|
+
}
|
|
385
|
+
function isChildrenOfCreateElement(context, node) {
|
|
386
|
+
const parent = node.parent;
|
|
387
|
+
if (parent == null || parent.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
388
|
+
if (!isCreateElementCall(context, parent)) return false;
|
|
389
|
+
return parent.arguments.slice(2).some((arg) => arg === node);
|
|
390
|
+
}
|
|
370
391
|
function isClassComponent(node) {
|
|
371
392
|
if ("superClass" in node && node.superClass != null) {
|
|
372
393
|
const re = /^(?:Pure)?Component$/u;
|
|
@@ -394,28 +415,28 @@ function isPureComponent(node) {
|
|
|
394
415
|
|
|
395
416
|
// src/component/component-lifecycle.ts
|
|
396
417
|
function isComponentDidMount(node) {
|
|
397
|
-
return
|
|
418
|
+
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidMount";
|
|
398
419
|
}
|
|
399
420
|
function isComponentWillUnmount(node) {
|
|
400
|
-
return
|
|
421
|
+
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillUnmount";
|
|
401
422
|
}
|
|
402
423
|
function isComponentDidCatch(node) {
|
|
403
|
-
return
|
|
424
|
+
return AST8.isMethodOrProperty(node) && !node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidCatch";
|
|
404
425
|
}
|
|
405
426
|
function isGetDerivedStateFromError(node) {
|
|
406
|
-
return
|
|
427
|
+
return AST8.isMethodOrProperty(node) && node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "getDerivedStateFromError";
|
|
407
428
|
}
|
|
408
429
|
function isGetDerivedStateFromProps(node) {
|
|
409
|
-
return
|
|
430
|
+
return AST8.isMethodOrProperty(node) && node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "getDerivedStateFromProps";
|
|
410
431
|
}
|
|
411
432
|
function isFunctionOfComponentDidMount(node) {
|
|
412
|
-
return
|
|
433
|
+
return AST8.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
|
|
413
434
|
}
|
|
414
435
|
function isFunctionOfComponentWillUnmount(node) {
|
|
415
|
-
return
|
|
436
|
+
return AST8.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
|
|
416
437
|
}
|
|
417
438
|
function isRenderMethodLike(node) {
|
|
418
|
-
return
|
|
439
|
+
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "render" && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration;
|
|
419
440
|
}
|
|
420
441
|
function isFunctionOfRenderMethod(node) {
|
|
421
442
|
if (!isRenderMethodLike(node.parent)) {
|
|
@@ -424,21 +445,46 @@ function isFunctionOfRenderMethod(node) {
|
|
|
424
445
|
return isClassComponent(node.parent.parent.parent);
|
|
425
446
|
}
|
|
426
447
|
function isInsideRenderMethod(node) {
|
|
427
|
-
return
|
|
448
|
+
return AST8.findParentNode(node, (node2) => isRenderMethodLike(node2) && isClassComponent(node2.parent.parent)) != null;
|
|
428
449
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
450
|
+
|
|
451
|
+
// src/component/hierarchy.ts
|
|
452
|
+
function hasValidHierarchy(context, node, hint) {
|
|
453
|
+
if (isChildrenOfCreateElement(context, node) || isFunctionOfRenderMethod(node)) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
if (hint & ERComponentHint.SkipMapCallback && AST8.isMapCallLoose(node.parent)) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
if (hint & ERComponentHint.SkipObjectMethod && isFunctionOfObjectMethod(node.parent)) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
if (hint & ERComponentHint.SkipClassMethod && isFunctionOfClassMethod(node.parent)) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
if (hint & ERComponentHint.SkipClassProperty && isFunctionOfClassProperty(node.parent)) {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
const boundaryNode = AST8.findParentNode(
|
|
469
|
+
node,
|
|
470
|
+
AST8.isOneOf([
|
|
471
|
+
AST_NODE_TYPES.JSXExpressionContainer,
|
|
472
|
+
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
473
|
+
AST_NODE_TYPES.FunctionExpression,
|
|
474
|
+
AST_NODE_TYPES.Property,
|
|
475
|
+
AST_NODE_TYPES.ClassBody
|
|
476
|
+
])
|
|
477
|
+
);
|
|
478
|
+
return boundaryNode == null || boundaryNode.type !== AST_NODE_TYPES.JSXExpressionContainer;
|
|
433
479
|
}
|
|
434
|
-
function
|
|
435
|
-
return
|
|
480
|
+
function isFunctionOfClassMethod(node) {
|
|
481
|
+
return (node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression) && node.parent.type === AST_NODE_TYPES.MethodDefinition;
|
|
436
482
|
}
|
|
437
|
-
function
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return
|
|
483
|
+
function isFunctionOfClassProperty(node) {
|
|
484
|
+
return (node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression) && node.parent.type === AST_NODE_TYPES.Property;
|
|
485
|
+
}
|
|
486
|
+
function isFunctionOfObjectMethod(node) {
|
|
487
|
+
return (node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression) && node.parent.type === AST_NODE_TYPES.Property && node.parent.parent.type === AST_NODE_TYPES.ObjectExpression;
|
|
442
488
|
}
|
|
443
489
|
|
|
444
490
|
// src/component/component-collector.ts
|
|
@@ -456,7 +502,7 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
456
502
|
const entry = functionEntries.at(-1);
|
|
457
503
|
if (entry == null) return;
|
|
458
504
|
if (!entry.isComponent) return functionEntries.pop();
|
|
459
|
-
const shouldDrop =
|
|
505
|
+
const shouldDrop = AST8.getNestedReturnStatements(entry.node.body).slice().reverse().some((r) => {
|
|
460
506
|
return context.sourceCode.getScope(r).block === entry.node && r.argument != null && !JSX3.isJSXValue(r.argument, jsxCtx, hint);
|
|
461
507
|
});
|
|
462
508
|
if (shouldDrop) {
|
|
@@ -481,10 +527,10 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
481
527
|
const entry = getCurrentEntry();
|
|
482
528
|
if (entry == null) return;
|
|
483
529
|
const { body } = entry.node;
|
|
484
|
-
const isComponent = hasNoneOrValidComponentName(entry.node
|
|
530
|
+
const isComponent = hasNoneOrValidComponentName(context, entry.node) && JSX3.isJSXValue(body, jsxCtx, hint) && hasValidHierarchy(context, entry.node, hint);
|
|
485
531
|
if (!isComponent) return;
|
|
486
|
-
const initPath =
|
|
487
|
-
const id = getFunctionComponentIdentifier(entry.node
|
|
532
|
+
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
533
|
+
const id = getFunctionComponentIdentifier(context, entry.node);
|
|
488
534
|
const name = getComponentNameFromIdentifier(id);
|
|
489
535
|
const key = getId();
|
|
490
536
|
components.set(key, {
|
|
@@ -521,11 +567,11 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
521
567
|
"ReturnStatement[type]"(node) {
|
|
522
568
|
const entry = getCurrentEntry();
|
|
523
569
|
if (entry == null) return;
|
|
524
|
-
const isComponent = hasNoneOrValidComponentName(entry.node
|
|
570
|
+
const isComponent = hasNoneOrValidComponentName(context, entry.node) && JSX3.isJSXValue(node.argument, jsxCtx, hint) && hasValidHierarchy(context, entry.node, hint);
|
|
525
571
|
if (!isComponent) return;
|
|
526
572
|
entry.isComponent = true;
|
|
527
|
-
const initPath =
|
|
528
|
-
const id = getFunctionComponentIdentifier(entry.node
|
|
573
|
+
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
574
|
+
const id = getFunctionComponentIdentifier(context, entry.node);
|
|
529
575
|
const name = getComponentNameFromIdentifier(id);
|
|
530
576
|
components.set(entry.key, {
|
|
531
577
|
id,
|
|
@@ -543,40 +589,12 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options =
|
|
|
543
589
|
};
|
|
544
590
|
return { ctx, listeners };
|
|
545
591
|
}
|
|
546
|
-
function hasValidHierarchy(node, context, hint) {
|
|
547
|
-
if (isChildrenOfCreateElement(node, context) || isFunctionOfRenderMethod(node)) {
|
|
548
|
-
return false;
|
|
549
|
-
}
|
|
550
|
-
if (hint & ERComponentHint.SkipMapCallback && AST9.isMapCallLoose(node.parent)) {
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
if (hint & ERComponentHint.SkipObjectMethod && AST9.isFunctionOfObjectMethod(node.parent)) {
|
|
554
|
-
return false;
|
|
555
|
-
}
|
|
556
|
-
if (hint & ERComponentHint.SkipClassMethod && AST9.isFunctionOfClassMethod(node.parent)) {
|
|
557
|
-
return false;
|
|
558
|
-
}
|
|
559
|
-
if (hint & ERComponentHint.SkipClassProperty && AST9.isFunctionOfClassProperty(node.parent)) {
|
|
560
|
-
return false;
|
|
561
|
-
}
|
|
562
|
-
const boundaryNode = AST9.findParentNode(
|
|
563
|
-
node,
|
|
564
|
-
AST9.isOneOf([
|
|
565
|
-
AST_NODE_TYPES.JSXExpressionContainer,
|
|
566
|
-
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
567
|
-
AST_NODE_TYPES.FunctionExpression,
|
|
568
|
-
AST_NODE_TYPES.Property,
|
|
569
|
-
AST_NODE_TYPES.ClassBody
|
|
570
|
-
])
|
|
571
|
-
);
|
|
572
|
-
return boundaryNode == null || boundaryNode.type !== AST_NODE_TYPES.JSXExpressionContainer;
|
|
573
|
-
}
|
|
574
592
|
function getComponentFlag(initPath) {
|
|
575
593
|
let flag = ERComponentFlag.None;
|
|
576
|
-
if (initPath != null &&
|
|
594
|
+
if (initPath != null && AST8.hasCallInFunctionInitPath("memo", initPath)) {
|
|
577
595
|
flag |= ERComponentFlag.Memo;
|
|
578
596
|
}
|
|
579
|
-
if (initPath != null &&
|
|
597
|
+
if (initPath != null && AST8.hasCallInFunctionInitPath("forwardRef", initPath)) {
|
|
580
598
|
flag |= ERComponentFlag.ForwardRef;
|
|
581
599
|
}
|
|
582
600
|
return flag;
|
|
@@ -593,7 +611,7 @@ function useComponentCollectorLegacy() {
|
|
|
593
611
|
if (!isClassComponent(node)) {
|
|
594
612
|
return;
|
|
595
613
|
}
|
|
596
|
-
const id =
|
|
614
|
+
const id = AST8.getClassIdentifier(node);
|
|
597
615
|
const key = getId();
|
|
598
616
|
const flag = isPureComponent(node) ? ERComponentFlag.PureComponent : ERComponentFlag.None;
|
|
599
617
|
components.set(
|
|
@@ -624,9 +642,9 @@ var ERPhaseRelevance = birecord({
|
|
|
624
642
|
setup: "cleanup"
|
|
625
643
|
});
|
|
626
644
|
var isInversePhase = dual(2, (a, b) => ERPhaseRelevance.get(a) === b);
|
|
627
|
-
function isRenderFunctionLoose(
|
|
645
|
+
function isRenderFunctionLoose(context, node) {
|
|
628
646
|
const { body, parent } = node;
|
|
629
|
-
if (
|
|
647
|
+
if (AST8.getFunctionIdentifier(node)?.name.startsWith("render")) {
|
|
630
648
|
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");
|
|
631
649
|
}
|
|
632
650
|
return JSX3.isJSXValue(
|
|
@@ -637,11 +655,11 @@ function isRenderFunctionLoose(node, context) {
|
|
|
637
655
|
JSX3.JSXValueHint.SkipNullLiteral | JSX3.JSXValueHint.SkipUndefined | JSX3.JSXValueHint.StrictLogical | JSX3.JSXValueHint.StrictConditional
|
|
638
656
|
);
|
|
639
657
|
}
|
|
640
|
-
function isRenderPropLoose(
|
|
658
|
+
function isRenderPropLoose(context, node) {
|
|
641
659
|
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier) {
|
|
642
660
|
return false;
|
|
643
661
|
}
|
|
644
|
-
return node.name.name.startsWith("render") && node.value?.type === AST_NODE_TYPES.JSXExpressionContainer &&
|
|
662
|
+
return node.name.name.startsWith("render") && node.value?.type === AST_NODE_TYPES.JSXExpressionContainer && AST8.isFunction(node.value.expression) && isRenderFunctionLoose(context, node.value.expression);
|
|
645
663
|
}
|
|
646
664
|
function isDirectValueOfRenderPropertyLoose(node) {
|
|
647
665
|
const matching = (node2) => {
|
|
@@ -653,7 +671,7 @@ function isDeclaredInRenderPropLoose(node) {
|
|
|
653
671
|
if (isDirectValueOfRenderPropertyLoose(node)) {
|
|
654
672
|
return true;
|
|
655
673
|
}
|
|
656
|
-
const parent =
|
|
674
|
+
const parent = AST8.findParentNode(node, AST8.is(AST_NODE_TYPES.JSXExpressionContainer))?.parent;
|
|
657
675
|
if (parent?.type !== AST_NODE_TYPES.JSXAttribute) {
|
|
658
676
|
return false;
|
|
659
677
|
}
|
|
@@ -661,17 +679,17 @@ function isDeclaredInRenderPropLoose(node) {
|
|
|
661
679
|
}
|
|
662
680
|
function isThisSetState(node) {
|
|
663
681
|
const { callee } = node;
|
|
664
|
-
return callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
682
|
+
return callee.type === AST_NODE_TYPES.MemberExpression && AST8.isThisExpression(callee.object) && callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "setState";
|
|
665
683
|
}
|
|
666
684
|
function isFunctionOfUseEffectSetup(node) {
|
|
667
685
|
if (node == null) return _;
|
|
668
686
|
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && node.parent.callee.type === AST_NODE_TYPES.Identifier && node.parent.arguments.at(0) === node && isUseEffectCallLoose(node.parent);
|
|
669
687
|
}
|
|
670
688
|
function isFunctionOfUseEffectCleanup(node) {
|
|
671
|
-
const nearReturn =
|
|
672
|
-
const nearFunction =
|
|
673
|
-
const nearFunctionOfReturn =
|
|
689
|
+
const nearReturn = AST8.findParentNode(node, AST8.is(AST_NODE_TYPES.ReturnStatement));
|
|
690
|
+
const nearFunction = AST8.findParentNode(node, AST8.isFunction);
|
|
691
|
+
const nearFunctionOfReturn = AST8.findParentNode(nearReturn, AST8.isFunction);
|
|
674
692
|
return nearFunction === nearFunctionOfReturn && isFunctionOfUseEffectSetup(nearFunction);
|
|
675
693
|
}
|
|
676
694
|
|
|
677
|
-
export { DEFAULT_COMPONENT_HINT, DISPLAY_NAME_ASSIGNMENT_SELECTOR, ERComponentFlag, ERComponentHint, 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, isComponentName, isComponentWillUnmount, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isDisplayNameAssignment, 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 };
|
|
695
|
+
export { DEFAULT_COMPONENT_HINT, DISPLAY_NAME_ASSIGNMENT_SELECTOR, ERComponentFlag, ERComponentHint, ERPhaseRelevance, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier, getFunctionComponentIdentifier, hasNoneOrValidComponentName, hasValidHierarchy, isCallFromReact, isCallFromReactMember, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElement, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentName, isComponentWillUnmount, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isDisplayNameAssignment, isForwardRef, isForwardRefCall, isFromReact, isFromReactLoose, isFromReactMember, isFromReactMemberLoose, isFromReactMemberStrict, isFromReactStrict, 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.28.0-beta.
|
|
3
|
+
"version": "1.28.0-beta.4",
|
|
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": {
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"@typescript-eslint/utils": "^8.25.0",
|
|
42
42
|
"birecord": "^0.1.1",
|
|
43
43
|
"ts-pattern": "^5.6.2",
|
|
44
|
-
"@eslint-react/ast": "1.28.0-beta.
|
|
45
|
-
"@eslint-react/eff": "1.28.0-beta.
|
|
46
|
-
"@eslint-react/
|
|
47
|
-
"@eslint-react/
|
|
48
|
-
"@eslint-react/var": "1.28.0-beta.
|
|
44
|
+
"@eslint-react/ast": "1.28.0-beta.4",
|
|
45
|
+
"@eslint-react/eff": "1.28.0-beta.4",
|
|
46
|
+
"@eslint-react/shared": "1.28.0-beta.4",
|
|
47
|
+
"@eslint-react/jsx": "1.28.0-beta.4",
|
|
48
|
+
"@eslint-react/var": "1.28.0-beta.4"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"tsup": "^8.3.6",
|