@eslint-react/core 1.24.0-beta.0 → 1.24.0-beta.16
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 +152 -140
- package/dist/index.d.ts +152 -140
- package/dist/index.js +179 -178
- package/dist/index.mjs +163 -168
- package/package.json +11 -11
package/dist/index.mjs
CHANGED
|
@@ -1,35 +1,13 @@
|
|
|
1
1
|
import * as AST8 from '@eslint-react/ast';
|
|
2
|
-
import { flip,
|
|
3
|
-
import * as
|
|
2
|
+
import { flip, dual, _, returnFalse } from '@eslint-react/eff';
|
|
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
|
|
7
|
+
import birecord from 'birecord';
|
|
7
8
|
|
|
8
9
|
// src/component/component-collector.ts
|
|
9
|
-
function
|
|
10
|
-
const name = JSX4.getElementName(node);
|
|
11
|
-
if (name === name.toLowerCase()) return [name, name];
|
|
12
|
-
const component = additionalComponents.findLast((c) => c.name === name || c.re.test(name));
|
|
13
|
-
if (component !== _) return [name, component.as];
|
|
14
|
-
if (polymorphicPropName == null) return [name, name];
|
|
15
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
16
|
-
const polymorphicProp = JSX4.findPropInAttributes(
|
|
17
|
-
polymorphicPropName,
|
|
18
|
-
initialScope,
|
|
19
|
-
node.attributes
|
|
20
|
-
);
|
|
21
|
-
if (polymorphicProp === _) return [name, name];
|
|
22
|
-
const polymorphicName = VAR.toResolved(JSX4.getPropValue(polymorphicProp, initialScope)).value;
|
|
23
|
-
if (typeof polymorphicName === "string") {
|
|
24
|
-
return [name, polymorphicName];
|
|
25
|
-
}
|
|
26
|
-
return [name, name];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// src/utils/get-id.ts
|
|
30
|
-
var id = 0n;
|
|
31
|
-
var getId = () => (id++).toString();
|
|
32
|
-
function isInitializedFromReact(name, initialScope, importSource = "react") {
|
|
10
|
+
function isInitializedFromReact(name, importSource, initialScope) {
|
|
33
11
|
return name.toLowerCase() === "react" || VAR.isInitializedFromSource(
|
|
34
12
|
name,
|
|
35
13
|
importSource,
|
|
@@ -47,12 +25,13 @@ function isFromReact(name) {
|
|
|
47
25
|
}
|
|
48
26
|
return node.name === name;
|
|
49
27
|
}
|
|
28
|
+
const importSource = settings.importSource ?? DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
50
29
|
const initialScope = context.sourceCode.getScope(node);
|
|
51
30
|
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
52
|
-
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);
|
|
53
32
|
}
|
|
54
33
|
if (node.name === name) {
|
|
55
|
-
return isInitializedFromReact(name,
|
|
34
|
+
return isInitializedFromReact(name, importSource, initialScope);
|
|
56
35
|
}
|
|
57
36
|
return false;
|
|
58
37
|
};
|
|
@@ -72,14 +51,15 @@ function isFromReactMember(memberName, name) {
|
|
|
72
51
|
}
|
|
73
52
|
return false;
|
|
74
53
|
}
|
|
54
|
+
const importSource = settings.importSource ?? DEFAULT_ESLINT_REACT_SETTINGS.importSource;
|
|
75
55
|
const initialScope = context.sourceCode.getScope(node);
|
|
76
56
|
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name) {
|
|
77
57
|
return false;
|
|
78
58
|
}
|
|
79
59
|
if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === memberName) {
|
|
80
|
-
return isInitializedFromReact(node.object.name,
|
|
60
|
+
return isInitializedFromReact(node.object.name, importSource, initialScope);
|
|
81
61
|
}
|
|
82
|
-
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) {
|
|
83
63
|
return node.object.property.name === memberName;
|
|
84
64
|
}
|
|
85
65
|
return false;
|
|
@@ -140,15 +120,15 @@ var isMemoCall = isReactAPICallWithName("memo");
|
|
|
140
120
|
|
|
141
121
|
// src/element/hierarchy.ts
|
|
142
122
|
function isInsideCreateElementProps(node, context) {
|
|
143
|
-
const call = AST8.
|
|
144
|
-
if (
|
|
145
|
-
const prop = AST8.
|
|
146
|
-
if (
|
|
123
|
+
const call = AST8.findParentNode(node, isCreateElementCall(context));
|
|
124
|
+
if (call == null) return false;
|
|
125
|
+
const prop = AST8.findParentNode(node, AST8.is(AST_NODE_TYPES.ObjectExpression));
|
|
126
|
+
if (prop == null) return false;
|
|
147
127
|
return prop === call.arguments[1];
|
|
148
128
|
}
|
|
149
129
|
function isChildrenOfCreateElement(node, context) {
|
|
150
130
|
const parent = node.parent;
|
|
151
|
-
if (
|
|
131
|
+
if (parent == null || parent.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
152
132
|
if (!isCreateElementCall(parent, context)) return false;
|
|
153
133
|
return parent.arguments.slice(2).some((arg) => arg === node);
|
|
154
134
|
}
|
|
@@ -161,12 +141,12 @@ function isReactHookName(name) {
|
|
|
161
141
|
|
|
162
142
|
// src/hook/is.ts
|
|
163
143
|
function isReactHook(node) {
|
|
164
|
-
if (node
|
|
165
|
-
const
|
|
166
|
-
return
|
|
144
|
+
if (node == null) return _;
|
|
145
|
+
const id = AST8.getFunctionIdentifier(node);
|
|
146
|
+
return id?.name != null && isReactHookName(id.name);
|
|
167
147
|
}
|
|
168
148
|
function isReactHookCall(node) {
|
|
169
|
-
if (node
|
|
149
|
+
if (node == null) return false;
|
|
170
150
|
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
171
151
|
return false;
|
|
172
152
|
}
|
|
@@ -179,22 +159,23 @@ function isReactHookCall(node) {
|
|
|
179
159
|
return false;
|
|
180
160
|
}
|
|
181
161
|
function isReactHookCallWithName(node, context) {
|
|
182
|
-
if (node
|
|
162
|
+
if (node == null) return returnFalse;
|
|
183
163
|
const settings = unsafeDecodeSettings(context.settings);
|
|
164
|
+
const importSource = settings.importSource ?? "react";
|
|
165
|
+
const initialScope = context.sourceCode.getScope(node);
|
|
184
166
|
return (name) => {
|
|
185
|
-
const initialScope = context.sourceCode.getScope(node);
|
|
186
167
|
switch (true) {
|
|
187
168
|
case (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
188
|
-
return !settings.strictImportCheck || isInitializedFromReact(name,
|
|
169
|
+
return !settings.strictImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
189
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):
|
|
190
|
-
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name,
|
|
171
|
+
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
191
172
|
default:
|
|
192
173
|
return false;
|
|
193
174
|
}
|
|
194
175
|
};
|
|
195
176
|
}
|
|
196
177
|
function isReactHookCallWithNameLoose(node) {
|
|
197
|
-
if (node
|
|
178
|
+
if (node == null) return returnFalse;
|
|
198
179
|
return (name) => {
|
|
199
180
|
switch (node.callee.type) {
|
|
200
181
|
case AST_NODE_TYPES.Identifier:
|
|
@@ -208,20 +189,21 @@ function isReactHookCallWithNameLoose(node) {
|
|
|
208
189
|
}
|
|
209
190
|
function isReactHookCallWithNameAlias(name, context, alias) {
|
|
210
191
|
const settings = unsafeDecodeSettings(context);
|
|
192
|
+
const importSource = settings.importSource ?? "react";
|
|
211
193
|
return (node) => {
|
|
212
194
|
const initialScope = context.sourceCode.getScope(node);
|
|
213
195
|
switch (true) {
|
|
214
196
|
case (node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === name):
|
|
215
|
-
return !settings.strictImportCheck || isInitializedFromReact(name,
|
|
197
|
+
return !settings.strictImportCheck || isInitializedFromReact(name, importSource, initialScope);
|
|
216
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):
|
|
217
|
-
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name,
|
|
199
|
+
return !settings.strictImportCheck || isInitializedFromReact(node.callee.object.name, importSource, initialScope);
|
|
218
200
|
default:
|
|
219
201
|
return alias.some(isReactHookCallWithNameLoose(node));
|
|
220
202
|
}
|
|
221
203
|
};
|
|
222
204
|
}
|
|
223
205
|
function isUseEffectCallLoose(node) {
|
|
224
|
-
if (node
|
|
206
|
+
if (node == null) return false;
|
|
225
207
|
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
226
208
|
return false;
|
|
227
209
|
}
|
|
@@ -252,20 +234,20 @@ var isUseTransitionCall = flip(isReactHookCallWithName)("useTransition");
|
|
|
252
234
|
|
|
253
235
|
// src/hook/hierarchy.ts
|
|
254
236
|
function isInsideReactHook(node) {
|
|
255
|
-
return isReactHook(AST8.
|
|
237
|
+
return isReactHook(AST8.findParentNode(node, AST8.isFunction));
|
|
256
238
|
}
|
|
257
239
|
function useHookCollector() {
|
|
258
240
|
const hooks = /* @__PURE__ */ new Map();
|
|
259
241
|
const fEntries = [];
|
|
260
242
|
const onFunctionEnter = (node) => {
|
|
261
|
-
const
|
|
262
|
-
const name =
|
|
263
|
-
if (name
|
|
243
|
+
const id = AST8.getFunctionIdentifier(node);
|
|
244
|
+
const name = id?.name;
|
|
245
|
+
if (name != null && isReactHookName(name)) {
|
|
264
246
|
const key = getId();
|
|
265
247
|
fEntries.push({ key, node });
|
|
266
248
|
hooks.set(key, {
|
|
267
|
-
|
|
268
|
-
|
|
249
|
+
id,
|
|
250
|
+
key,
|
|
269
251
|
kind: "function",
|
|
270
252
|
name,
|
|
271
253
|
node,
|
|
@@ -297,26 +279,20 @@ function useHookCollector() {
|
|
|
297
279
|
return;
|
|
298
280
|
}
|
|
299
281
|
const fEntry = fEntries.at(-1);
|
|
300
|
-
if (fEntry?.key
|
|
282
|
+
if (fEntry?.key == null) {
|
|
301
283
|
return;
|
|
302
284
|
}
|
|
303
285
|
const hook = hooks.get(fEntry.key);
|
|
304
|
-
if (hook
|
|
286
|
+
if (hook == null) {
|
|
305
287
|
return;
|
|
306
288
|
}
|
|
307
|
-
|
|
308
|
-
...hook,
|
|
309
|
-
hookCalls: [
|
|
310
|
-
...hook.hookCalls,
|
|
311
|
-
node
|
|
312
|
-
]
|
|
313
|
-
});
|
|
289
|
+
hook.hookCalls.push(node);
|
|
314
290
|
}
|
|
315
291
|
};
|
|
316
292
|
return { ctx, listeners };
|
|
317
293
|
}
|
|
318
294
|
var ERComponentHint = {
|
|
319
|
-
...
|
|
295
|
+
...JSX3.JSXValueHint,
|
|
320
296
|
// 1n << 0n - 1n << 63n are reserved for JSXValueHint
|
|
321
297
|
// Skip function component created by React.memo
|
|
322
298
|
SkipMemo: 1n << 64n,
|
|
@@ -332,6 +308,17 @@ var ERComponentHint = {
|
|
|
332
308
|
SkipClassProperty: 1n << 69n
|
|
333
309
|
};
|
|
334
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
|
+
}
|
|
335
322
|
|
|
336
323
|
// src/component/component-flag.ts
|
|
337
324
|
var ERClassComponentFlag = {
|
|
@@ -359,7 +346,7 @@ function isComponentWrapperCall(node, context) {
|
|
|
359
346
|
}
|
|
360
347
|
function getFunctionComponentIdentifier(node, context) {
|
|
361
348
|
const functionId = AST8.getFunctionIdentifier(node);
|
|
362
|
-
if (functionId) {
|
|
349
|
+
if (functionId != null) {
|
|
363
350
|
return functionId;
|
|
364
351
|
}
|
|
365
352
|
const { parent } = node;
|
|
@@ -371,17 +358,8 @@ function getFunctionComponentIdentifier(node, context) {
|
|
|
371
358
|
}
|
|
372
359
|
return _;
|
|
373
360
|
}
|
|
374
|
-
|
|
375
|
-
// src/component/component-name.ts
|
|
376
|
-
var RE_COMPONENT_NAME = /^_?[A-Z]/u;
|
|
377
|
-
function getComponentNameFromIdentifier(node) {
|
|
378
|
-
return Array.isArray(node) ? node.map((n) => n.name).join(".") : node.name;
|
|
379
|
-
}
|
|
380
|
-
function isComponentName(name) {
|
|
381
|
-
return RE_COMPONENT_NAME.test(name);
|
|
382
|
-
}
|
|
383
361
|
function isClassComponent(node) {
|
|
384
|
-
if ("superClass" in node && node.superClass) {
|
|
362
|
+
if ("superClass" in node && node.superClass != null) {
|
|
385
363
|
const re = /^(?:Pure)?Component$/u;
|
|
386
364
|
switch (true) {
|
|
387
365
|
case node.superClass.type === AST_NODE_TYPES.Identifier:
|
|
@@ -393,7 +371,7 @@ function isClassComponent(node) {
|
|
|
393
371
|
return false;
|
|
394
372
|
}
|
|
395
373
|
function isPureComponent(node) {
|
|
396
|
-
if ("superClass" in node && node.superClass) {
|
|
374
|
+
if ("superClass" in node && node.superClass != null) {
|
|
397
375
|
const re = /^PureComponent$/u;
|
|
398
376
|
switch (true) {
|
|
399
377
|
case node.superClass.type === AST_NODE_TYPES.Identifier:
|
|
@@ -405,9 +383,30 @@ function isPureComponent(node) {
|
|
|
405
383
|
return false;
|
|
406
384
|
}
|
|
407
385
|
|
|
408
|
-
// src/component/component-
|
|
386
|
+
// src/component/component-lifecycle.ts
|
|
387
|
+
function isComponentDidMount(node) {
|
|
388
|
+
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidMount";
|
|
389
|
+
}
|
|
390
|
+
function isComponentWillUnmount(node) {
|
|
391
|
+
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillUnmount";
|
|
392
|
+
}
|
|
393
|
+
function isComponentDidCatch(node) {
|
|
394
|
+
return AST8.isMethodOrProperty(node) && !node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidCatch";
|
|
395
|
+
}
|
|
396
|
+
function isGetDerivedStateFromError(node) {
|
|
397
|
+
return AST8.isMethodOrProperty(node) && node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "getDerivedStateFromError";
|
|
398
|
+
}
|
|
399
|
+
function isGetDerivedStateFromProps(node) {
|
|
400
|
+
return AST8.isMethodOrProperty(node) && node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "getDerivedStateFromProps";
|
|
401
|
+
}
|
|
402
|
+
function isFunctionOfComponentDidMount(node) {
|
|
403
|
+
return AST8.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
|
|
404
|
+
}
|
|
405
|
+
function isFunctionOfComponentWillUnmount(node) {
|
|
406
|
+
return AST8.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
|
|
407
|
+
}
|
|
409
408
|
function isRenderMethodLike(node) {
|
|
410
|
-
return (node
|
|
409
|
+
return AST8.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "render" && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration;
|
|
411
410
|
}
|
|
412
411
|
function isFunctionOfRenderMethod(node) {
|
|
413
412
|
if (!isRenderMethodLike(node.parent)) {
|
|
@@ -416,14 +415,20 @@ function isFunctionOfRenderMethod(node) {
|
|
|
416
415
|
return isClassComponent(node.parent.parent.parent);
|
|
417
416
|
}
|
|
418
417
|
function isInsideRenderMethod(node) {
|
|
419
|
-
return
|
|
418
|
+
return AST8.findParentNode(node, (node2) => isRenderMethodLike(node2) && isClassComponent(node2.parent.parent)) != null;
|
|
419
|
+
}
|
|
420
|
+
var RE_COMPONENT_NAME = /^_?[A-Z]/u;
|
|
421
|
+
function getComponentNameFromIdentifier(node) {
|
|
422
|
+
if (node == null) return _;
|
|
423
|
+
return Array.isArray(node) ? node.map((n) => n.name).join(".") : node.name;
|
|
424
|
+
}
|
|
425
|
+
function isComponentName(name) {
|
|
426
|
+
return RE_COMPONENT_NAME.test(name);
|
|
420
427
|
}
|
|
421
|
-
|
|
422
|
-
// src/component/misc.ts
|
|
423
428
|
function hasNoneOrValidComponentName(node, context) {
|
|
424
|
-
const
|
|
425
|
-
if (
|
|
426
|
-
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;
|
|
427
432
|
return name != null && isComponentName(name);
|
|
428
433
|
}
|
|
429
434
|
|
|
@@ -454,19 +459,20 @@ function hasValidHierarchy(node, context, hint) {
|
|
|
454
459
|
AST_NODE_TYPES.ClassBody
|
|
455
460
|
])
|
|
456
461
|
);
|
|
457
|
-
return
|
|
462
|
+
return boundaryNode == null || boundaryNode.type !== AST_NODE_TYPES.JSXExpressionContainer;
|
|
458
463
|
}
|
|
459
464
|
function getComponentFlag(initPath) {
|
|
460
465
|
let flag = ERFunctionComponentFlag.None;
|
|
461
|
-
if (initPath && AST8.hasCallInFunctionInitPath("memo"
|
|
466
|
+
if (initPath != null && AST8.hasCallInFunctionInitPath("memo", initPath)) {
|
|
462
467
|
flag |= ERFunctionComponentFlag.Memo;
|
|
463
468
|
}
|
|
464
|
-
if (initPath && AST8.hasCallInFunctionInitPath("forwardRef"
|
|
469
|
+
if (initPath != null && AST8.hasCallInFunctionInitPath("forwardRef", initPath)) {
|
|
465
470
|
flag |= ERFunctionComponentFlag.ForwardRef;
|
|
466
471
|
}
|
|
467
472
|
return flag;
|
|
468
473
|
}
|
|
469
|
-
function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
474
|
+
function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT, options = {}) {
|
|
475
|
+
const { collectDisplayName = false, collectHookCalls = false } = options;
|
|
470
476
|
const jsxCtx = { getScope: (node) => context.sourceCode.getScope(node) };
|
|
471
477
|
const components = /* @__PURE__ */ new Map();
|
|
472
478
|
const functionEntries = [];
|
|
@@ -477,12 +483,12 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
477
483
|
};
|
|
478
484
|
const onFunctionExit = () => {
|
|
479
485
|
const entry = functionEntries.at(-1);
|
|
480
|
-
if (
|
|
486
|
+
if (entry == null) return;
|
|
481
487
|
if (!entry.isComponent) {
|
|
482
488
|
return functionEntries.pop();
|
|
483
489
|
}
|
|
484
490
|
const shouldDrop = AST8.getNestedReturnStatements(entry.node.body).slice().reverse().some((r) => {
|
|
485
|
-
return context.sourceCode.getScope(r).block === entry.node &&
|
|
491
|
+
return context.sourceCode.getScope(r).block === entry.node && r.argument != null && !JSX3.isJSXValue(r.argument, jsxCtx, hint);
|
|
486
492
|
});
|
|
487
493
|
if (shouldDrop) {
|
|
488
494
|
components.delete(entry.key);
|
|
@@ -504,21 +510,21 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
504
510
|
":function[type]:exit": onFunctionExit,
|
|
505
511
|
"ArrowFunctionExpression[type][body.type!='BlockStatement']"() {
|
|
506
512
|
const entry = getCurrentEntry();
|
|
507
|
-
if (
|
|
513
|
+
if (entry == null) {
|
|
508
514
|
return;
|
|
509
515
|
}
|
|
510
516
|
const { body } = entry.node;
|
|
511
|
-
const isComponent = hasNoneOrValidComponentName(entry.node, context) &&
|
|
517
|
+
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX3.isJSXValue(body, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
512
518
|
if (!isComponent) {
|
|
513
519
|
return;
|
|
514
520
|
}
|
|
515
521
|
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
516
|
-
const
|
|
517
|
-
const name =
|
|
522
|
+
const id = getFunctionComponentIdentifier(entry.node, context);
|
|
523
|
+
const name = getComponentNameFromIdentifier(id);
|
|
518
524
|
const key = getId();
|
|
519
525
|
components.set(key, {
|
|
520
|
-
|
|
521
|
-
|
|
526
|
+
id,
|
|
527
|
+
key,
|
|
522
528
|
kind: "function",
|
|
523
529
|
name,
|
|
524
530
|
node: entry.node,
|
|
@@ -529,46 +535,47 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
529
535
|
initPath
|
|
530
536
|
});
|
|
531
537
|
},
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
+
...collectDisplayName ? {
|
|
539
|
+
[COMPONENT_DISPLAY_NAME_ASSIGNMENT_SELECTOR](node) {
|
|
540
|
+
const { left, right } = node;
|
|
541
|
+
if (left.type !== AST_NODE_TYPES.MemberExpression) return;
|
|
542
|
+
const componentName = left.object.type === AST_NODE_TYPES.Identifier ? left.object.name : _;
|
|
543
|
+
const component = [...components.values()].findLast(({ name }) => name != null && name === componentName);
|
|
544
|
+
if (component == null) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
component.displayName = right;
|
|
538
548
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return;
|
|
549
|
+
} : {},
|
|
550
|
+
...collectHookCalls ? {
|
|
551
|
+
"CallExpression[type]:exit"(node) {
|
|
552
|
+
if (!isReactHookCall(node)) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const entry = getCurrentEntry();
|
|
556
|
+
if (entry == null) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
entry.hookCalls.push(node);
|
|
551
560
|
}
|
|
552
|
-
|
|
553
|
-
functionEntries.push({ ...entry, hookCalls: [...entry.hookCalls, node] });
|
|
554
|
-
},
|
|
561
|
+
} : {},
|
|
555
562
|
"ReturnStatement[type]"(node) {
|
|
556
563
|
const entry = getCurrentEntry();
|
|
557
|
-
if (
|
|
564
|
+
if (entry == null) {
|
|
558
565
|
return;
|
|
559
566
|
}
|
|
560
|
-
const isComponent = hasNoneOrValidComponentName(entry.node, context) &&
|
|
567
|
+
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX3.isJSXValue(node.argument, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
561
568
|
if (!isComponent) {
|
|
562
569
|
return;
|
|
563
570
|
}
|
|
564
571
|
functionEntries.pop();
|
|
565
572
|
functionEntries.push({ ...entry, isComponent });
|
|
566
573
|
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
567
|
-
const
|
|
568
|
-
const name =
|
|
574
|
+
const id = getFunctionComponentIdentifier(entry.node, context);
|
|
575
|
+
const name = getComponentNameFromIdentifier(id);
|
|
569
576
|
components.set(entry.key, {
|
|
570
|
-
|
|
571
|
-
|
|
577
|
+
id,
|
|
578
|
+
key: entry.key,
|
|
572
579
|
kind: "function",
|
|
573
580
|
name,
|
|
574
581
|
node: entry.node,
|
|
@@ -594,16 +601,16 @@ function useComponentCollectorLegacy() {
|
|
|
594
601
|
if (!isClassComponent(node)) {
|
|
595
602
|
return;
|
|
596
603
|
}
|
|
597
|
-
const
|
|
604
|
+
const id = AST8.getClassIdentifier(node);
|
|
598
605
|
const key = getId();
|
|
599
606
|
const flag = isPureComponent(node) ? ERClassComponentFlag.PureComponent : ERClassComponentFlag.None;
|
|
600
607
|
components.set(
|
|
601
608
|
key,
|
|
602
609
|
{
|
|
603
|
-
|
|
604
|
-
|
|
610
|
+
id,
|
|
611
|
+
key,
|
|
605
612
|
kind: "class",
|
|
606
|
-
name:
|
|
613
|
+
name: id?.name,
|
|
607
614
|
node,
|
|
608
615
|
// TODO: Get displayName of class component
|
|
609
616
|
displayName: _,
|
|
@@ -620,60 +627,22 @@ function useComponentCollectorLegacy() {
|
|
|
620
627
|
};
|
|
621
628
|
return { ctx, listeners };
|
|
622
629
|
}
|
|
623
|
-
function isSetupFunction(node) {
|
|
624
|
-
if (node === _) return _;
|
|
625
|
-
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);
|
|
626
|
-
}
|
|
627
|
-
function isCleanupFunction(node) {
|
|
628
|
-
const nearReturn = AST8.findParentNodeGuard(node, AST8.is(AST_NODE_TYPES.ReturnStatement));
|
|
629
|
-
const nearFunction = AST8.findParentNodeGuard(node, AST8.isFunction);
|
|
630
|
-
const nearFunctionOfReturn = AST8.findParentNodeGuard(nearReturn, AST8.isFunction);
|
|
631
|
-
return nearFunction === nearFunctionOfReturn && isSetupFunction(nearFunction);
|
|
632
|
-
}
|
|
633
|
-
function isComponentDidMount(node) {
|
|
634
|
-
return AST8.isOneOf([AST_NODE_TYPES.MethodDefinition, AST_NODE_TYPES.PropertyDefinition])(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidMount";
|
|
635
|
-
}
|
|
636
|
-
function isComponentWillUnmount(node) {
|
|
637
|
-
return AST8.isOneOf([AST_NODE_TYPES.MethodDefinition, AST_NODE_TYPES.PropertyDefinition])(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillUnmount";
|
|
638
|
-
}
|
|
639
|
-
function isComponentDidMountFunction(node) {
|
|
640
|
-
return AST8.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
|
|
641
|
-
}
|
|
642
|
-
function isComponentWillUnmountFunction(node) {
|
|
643
|
-
return AST8.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
|
|
644
|
-
}
|
|
645
630
|
var ERPhaseRelevance = birecord({
|
|
646
631
|
mount: "unmount",
|
|
647
632
|
setup: "cleanup"
|
|
648
633
|
});
|
|
649
634
|
var isInversePhase = dual(2, (a, b) => ERPhaseRelevance.get(a) === b);
|
|
650
|
-
function isDirectValueOfRenderPropertyLoose(node) {
|
|
651
|
-
const matching = (node2) => {
|
|
652
|
-
return node2.type === AST_NODE_TYPES.Property && node2.key.type === AST_NODE_TYPES.Identifier && node2.key.name.startsWith("render");
|
|
653
|
-
};
|
|
654
|
-
return matching(node) || !!node.parent && matching(node.parent);
|
|
655
|
-
}
|
|
656
|
-
function isDeclaredInRenderPropLoose(node) {
|
|
657
|
-
if (isDirectValueOfRenderPropertyLoose(node)) {
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
const parent = AST8.findParentNodeGuard(node, AST8.is(AST_NODE_TYPES.JSXExpressionContainer))?.parent;
|
|
661
|
-
if (parent?.type !== AST_NODE_TYPES.JSXAttribute) {
|
|
662
|
-
return false;
|
|
663
|
-
}
|
|
664
|
-
return parent.name.type === AST_NODE_TYPES.JSXIdentifier && parent.name.name.startsWith("render");
|
|
665
|
-
}
|
|
666
635
|
function isRenderFunctionLoose(node, context) {
|
|
667
636
|
const { body, parent } = node;
|
|
668
637
|
if (AST8.getFunctionIdentifier(node)?.name.startsWith("render")) {
|
|
669
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");
|
|
670
639
|
}
|
|
671
|
-
return
|
|
640
|
+
return JSX3.isJSXValue(
|
|
672
641
|
body,
|
|
673
642
|
{
|
|
674
643
|
getScope: (node2) => context.sourceCode.getScope(node2)
|
|
675
644
|
},
|
|
676
|
-
|
|
645
|
+
JSX3.JSXValueHint.SkipNullLiteral | JSX3.JSXValueHint.SkipUndefined | JSX3.JSXValueHint.StrictLogical | JSX3.JSXValueHint.StrictConditional
|
|
677
646
|
);
|
|
678
647
|
}
|
|
679
648
|
function isRenderPropLoose(node, context) {
|
|
@@ -682,9 +651,35 @@ function isRenderPropLoose(node, context) {
|
|
|
682
651
|
}
|
|
683
652
|
return node.name.name.startsWith("render") && node.value?.type === AST_NODE_TYPES.JSXExpressionContainer && AST8.isFunction(node.value.expression) && isRenderFunctionLoose(node.value.expression, context);
|
|
684
653
|
}
|
|
654
|
+
function isDirectValueOfRenderPropertyLoose(node) {
|
|
655
|
+
const matching = (node2) => {
|
|
656
|
+
return node2.type === AST_NODE_TYPES.Property && node2.key.type === AST_NODE_TYPES.Identifier && node2.key.name.startsWith("render");
|
|
657
|
+
};
|
|
658
|
+
return matching(node) || node.parent != null && matching(node.parent);
|
|
659
|
+
}
|
|
660
|
+
function isDeclaredInRenderPropLoose(node) {
|
|
661
|
+
if (isDirectValueOfRenderPropertyLoose(node)) {
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
const parent = AST8.findParentNode(node, AST8.is(AST_NODE_TYPES.JSXExpressionContainer))?.parent;
|
|
665
|
+
if (parent?.type !== AST_NODE_TYPES.JSXAttribute) {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
return parent.name.type === AST_NODE_TYPES.JSXIdentifier && parent.name.name.startsWith("render");
|
|
669
|
+
}
|
|
685
670
|
function isThisSetState(node) {
|
|
686
671
|
const { callee } = node;
|
|
687
672
|
return callee.type === AST_NODE_TYPES.MemberExpression && AST8.isThisExpression(callee.object) && callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "setState";
|
|
688
673
|
}
|
|
674
|
+
function isFunctionOfUseEffectSetup(node) {
|
|
675
|
+
if (node == null) return _;
|
|
676
|
+
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);
|
|
677
|
+
}
|
|
678
|
+
function isFunctionOfUseEffectCleanup(node) {
|
|
679
|
+
const nearReturn = AST8.findParentNode(node, AST8.is(AST_NODE_TYPES.ReturnStatement));
|
|
680
|
+
const nearFunction = AST8.findParentNode(node, AST8.isFunction);
|
|
681
|
+
const nearFunctionOfReturn = AST8.findParentNode(nearReturn, AST8.isFunction);
|
|
682
|
+
return nearFunction === nearFunctionOfReturn && isFunctionOfUseEffectSetup(nearFunction);
|
|
683
|
+
}
|
|
689
684
|
|
|
690
|
-
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-beta.
|
|
3
|
+
"version": "1.24.0-beta.16",
|
|
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": {
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
"./package.json"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@typescript-eslint/scope-manager": "^8.
|
|
37
|
-
"@typescript-eslint/type-utils": "^8.
|
|
38
|
-
"@typescript-eslint/types": "^8.
|
|
39
|
-
"@typescript-eslint/utils": "^8.
|
|
36
|
+
"@typescript-eslint/scope-manager": "^8.20.0",
|
|
37
|
+
"@typescript-eslint/type-utils": "^8.20.0",
|
|
38
|
+
"@typescript-eslint/types": "^8.20.0",
|
|
39
|
+
"@typescript-eslint/utils": "^8.20.0",
|
|
40
|
+
"birecord": "^0.1.1",
|
|
40
41
|
"ts-pattern": "^5.6.0",
|
|
41
|
-
"@eslint-react/
|
|
42
|
-
"@eslint-react/
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/var": "1.24.0-beta.0"
|
|
42
|
+
"@eslint-react/eff": "1.24.0-beta.16",
|
|
43
|
+
"@eslint-react/jsx": "1.24.0-beta.16",
|
|
44
|
+
"@eslint-react/shared": "1.24.0-beta.16",
|
|
45
|
+
"@eslint-react/var": "1.24.0-beta.16",
|
|
46
|
+
"@eslint-react/ast": "1.24.0-beta.16"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"tsup": "^8.3.5",
|