@eslint-react/core 1.23.2 → 1.23.3-beta.2
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 +23 -19
- package/dist/index.d.ts +23 -19
- package/dist/index.js +227 -175
- package/dist/index.mjs +191 -140
- package/package.json +9 -9
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as AST8 from '@eslint-react/ast';
|
|
2
2
|
import { F, isString, O } from '@eslint-react/eff';
|
|
3
3
|
import * as JSX4 from '@eslint-react/jsx';
|
|
4
4
|
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
@@ -10,10 +10,14 @@ import { birecord } from '@eslint-react/types';
|
|
|
10
10
|
// src/component/component-collector.ts
|
|
11
11
|
function getElementRepresentName(node, context) {
|
|
12
12
|
const rawElementName = JSX4.getElementName(node);
|
|
13
|
-
if (rawElementName === rawElementName.toLowerCase())
|
|
13
|
+
if (rawElementName === rawElementName.toLowerCase()) {
|
|
14
|
+
return rawElementName;
|
|
15
|
+
}
|
|
14
16
|
const { components, polymorphicPropName } = getSettingsFromContext(context);
|
|
15
17
|
const asElementName = components.get(rawElementName);
|
|
16
|
-
if (isString(asElementName))
|
|
18
|
+
if (isString(asElementName)) {
|
|
19
|
+
return asElementName;
|
|
20
|
+
}
|
|
17
21
|
return F.pipe(
|
|
18
22
|
O.fromNullable(polymorphicPropName),
|
|
19
23
|
O.flatMap(JSX4.findPropInAttributes(node.attributes, context.sourceCode.getScope(node))),
|
|
@@ -58,15 +62,21 @@ function isFromReactMember(memberName, name) {
|
|
|
58
62
|
return (node, context) => {
|
|
59
63
|
const settings = unsafeDecodeSettings(context.settings);
|
|
60
64
|
if (!settings.strictImportCheck) {
|
|
61
|
-
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name)
|
|
62
|
-
|
|
65
|
+
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === memberName) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
63
71
|
if (node.object.type === AST_NODE_TYPES.MemberExpression && node.object.object.type === AST_NODE_TYPES.Identifier && node.object.property.type === AST_NODE_TYPES.Identifier) {
|
|
64
72
|
return node.object.property.name === memberName;
|
|
65
73
|
}
|
|
66
74
|
return false;
|
|
67
75
|
}
|
|
68
76
|
const initialScope = context.sourceCode.getScope(node);
|
|
69
|
-
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name)
|
|
77
|
+
if (node.property.type !== AST_NODE_TYPES.Identifier || node.property.name !== name) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
70
80
|
if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === memberName) {
|
|
71
81
|
return isInitializedFromReact(node.object.name, initialScope, settings.importSource);
|
|
72
82
|
}
|
|
@@ -78,15 +88,23 @@ function isFromReactMember(memberName, name) {
|
|
|
78
88
|
}
|
|
79
89
|
function isCallFromReact(name) {
|
|
80
90
|
return F.dual(2, (node, context) => {
|
|
81
|
-
if (node.type !== AST_NODE_TYPES.CallExpression)
|
|
82
|
-
|
|
91
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (!AST8.isOneOf([AST_NODE_TYPES.Identifier, AST_NODE_TYPES.MemberExpression])(node.callee)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
83
97
|
return isFromReact(name)(node.callee, context);
|
|
84
98
|
});
|
|
85
99
|
}
|
|
86
100
|
function isCallFromReactMember(pragmaMemberName, name) {
|
|
87
101
|
return F.dual(2, (node, context) => {
|
|
88
|
-
if (node.type !== AST_NODE_TYPES.CallExpression)
|
|
89
|
-
|
|
102
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (!AST8.is(AST_NODE_TYPES.MemberExpression)(node.callee)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
90
108
|
return isFromReactMember(pragmaMemberName, name)(node.callee, context);
|
|
91
109
|
});
|
|
92
110
|
}
|
|
@@ -125,8 +143,8 @@ var isMemoCall = isReactAPICallWithName("memo");
|
|
|
125
143
|
function isInsideCreateElementProps(node, context) {
|
|
126
144
|
return F.pipe(
|
|
127
145
|
O.Do,
|
|
128
|
-
O.bind("call", () =>
|
|
129
|
-
O.bind("prop", () =>
|
|
146
|
+
O.bind("call", () => AST8.findParentNodeGuard(node, isCreateElementCall(context))),
|
|
147
|
+
O.bind("prop", () => AST8.findParentNodeGuard(node, AST8.is(AST_NODE_TYPES.ObjectExpression))),
|
|
130
148
|
O.bind("arg1", ({ call }) => O.fromNullable(call.arguments[1])),
|
|
131
149
|
O.exists(({ arg1, prop }) => prop === arg1)
|
|
132
150
|
);
|
|
@@ -134,7 +152,7 @@ function isInsideCreateElementProps(node, context) {
|
|
|
134
152
|
function isChildrenOfCreateElement(node, context) {
|
|
135
153
|
return F.pipe(
|
|
136
154
|
O.fromNullable(node.parent),
|
|
137
|
-
O.filter(
|
|
155
|
+
O.filter(AST8.is(AST_NODE_TYPES.CallExpression)),
|
|
138
156
|
O.filter(isCreateElementCall(context)),
|
|
139
157
|
O.exists(
|
|
140
158
|
(n) => n.arguments.slice(2).some((arg) => arg === node)
|
|
@@ -151,14 +169,18 @@ function isReactHookName(name) {
|
|
|
151
169
|
// src/hook/is.ts
|
|
152
170
|
function isReactHook(node) {
|
|
153
171
|
return F.pipe(
|
|
154
|
-
|
|
172
|
+
AST8.getFunctionIdentifier(node),
|
|
155
173
|
O.flatMapNullable((id2) => id2.name),
|
|
156
174
|
O.exists(isReactHookName)
|
|
157
175
|
);
|
|
158
176
|
}
|
|
159
177
|
function isReactHookCall(node) {
|
|
160
|
-
if (node.type !== AST_NODE_TYPES.CallExpression)
|
|
161
|
-
|
|
178
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (node.callee.type === AST_NODE_TYPES.Identifier) {
|
|
182
|
+
return isReactHookName(node.callee.name);
|
|
183
|
+
}
|
|
162
184
|
if (node.callee.type === AST_NODE_TYPES.MemberExpression) {
|
|
163
185
|
return node.callee.property.type === AST_NODE_TYPES.Identifier && isReactHookName(node.callee.property.name);
|
|
164
186
|
}
|
|
@@ -205,7 +227,9 @@ function isReactHookCallWithNameAlias(name, context, alias) {
|
|
|
205
227
|
};
|
|
206
228
|
}
|
|
207
229
|
function isUseEffectCallLoose(node) {
|
|
208
|
-
if (node.type !== AST_NODE_TYPES.CallExpression)
|
|
230
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
209
233
|
switch (node.callee.type) {
|
|
210
234
|
case AST_NODE_TYPES.Identifier:
|
|
211
235
|
return /^use\w*Effect$/u.test(node.callee.name);
|
|
@@ -233,13 +257,13 @@ var isUseTransitionCall = F.flip(isReactHookCallWithName)("useTransition");
|
|
|
233
257
|
|
|
234
258
|
// src/hook/hierarchy.ts
|
|
235
259
|
function isInsideReactHook(node) {
|
|
236
|
-
return O.exists(
|
|
260
|
+
return O.exists(AST8.findParentNodeGuard(node, AST8.isFunction), isReactHook);
|
|
237
261
|
}
|
|
238
262
|
function useHookCollector() {
|
|
239
263
|
const hooks = /* @__PURE__ */ new Map();
|
|
240
264
|
const fStack = [];
|
|
241
265
|
const onFunctionEnter = (node) => {
|
|
242
|
-
const id2 =
|
|
266
|
+
const id2 = AST8.getFunctionIdentifier(node);
|
|
243
267
|
const name = O.flatMapNullable(id2, (id3) => id3.name);
|
|
244
268
|
if (O.isSome(id2) && O.isSome(name) && isReactHookName(name.value)) {
|
|
245
269
|
const key = getId();
|
|
@@ -273,9 +297,13 @@ function useHookCollector() {
|
|
|
273
297
|
":function[type]": onFunctionEnter,
|
|
274
298
|
":function[type]:exit": onFunctionExit,
|
|
275
299
|
"CallExpression[type]"(node) {
|
|
276
|
-
if (!isReactHookCall(node))
|
|
300
|
+
if (!isReactHookCall(node)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
277
303
|
const [fNode, hookId] = fStack.at(-1) ?? [];
|
|
278
|
-
if (!fNode || !hookId)
|
|
304
|
+
if (!fNode || !hookId) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
279
307
|
F.pipe(
|
|
280
308
|
O.Do,
|
|
281
309
|
O.bind("id", () => hookId),
|
|
@@ -310,7 +338,7 @@ var ERComponentHint = {
|
|
|
310
338
|
// Skip function component defined on class property
|
|
311
339
|
SkipClassProperty: 1n << 69n
|
|
312
340
|
};
|
|
313
|
-
var DEFAULT_COMPONENT_HINT = 0n | ERComponentHint.SkipBooleanLiteral | ERComponentHint.SkipMapCallback | ERComponentHint.SkipNumberLiteral | ERComponentHint.SkipStringLiteral | ERComponentHint.
|
|
341
|
+
var DEFAULT_COMPONENT_HINT = 0n | ERComponentHint.SkipBooleanLiteral | ERComponentHint.SkipMapCallback | ERComponentHint.SkipNumberLiteral | ERComponentHint.SkipStringLiteral | ERComponentHint.SkipUndefined | ERComponentHint.SkipEmptyArray | ERComponentHint.StrictArray | ERComponentHint.StrictConditional | ERComponentHint.StrictLogical;
|
|
314
342
|
|
|
315
343
|
// src/component/component-flag.ts
|
|
316
344
|
var ERClassComponentFlag = {
|
|
@@ -331,12 +359,16 @@ var ERFunctionComponentFlag = {
|
|
|
331
359
|
// Async: 1n << 4n,
|
|
332
360
|
};
|
|
333
361
|
function isComponentWrapperCall(node, context) {
|
|
334
|
-
if (node.type !== AST_NODE_TYPES.CallExpression)
|
|
362
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
335
365
|
return isMemoCall(node, context) || isForwardRefCall(node, context) || isReactHookCallWithNameLoose(node)("useCallback");
|
|
336
366
|
}
|
|
337
367
|
function getFunctionComponentIdentifier(node, context) {
|
|
338
|
-
const functionId =
|
|
339
|
-
if (O.isSome(functionId))
|
|
368
|
+
const functionId = AST8.getFunctionIdentifier(node);
|
|
369
|
+
if (O.isSome(functionId)) {
|
|
370
|
+
return functionId;
|
|
371
|
+
}
|
|
340
372
|
const { parent } = node;
|
|
341
373
|
if (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(parent, context) && parent.parent.type === AST_NODE_TYPES.VariableDeclarator && parent.parent.id.type === AST_NODE_TYPES.Identifier) {
|
|
342
374
|
return O.some(parent.parent.id);
|
|
@@ -356,98 +388,42 @@ function isComponentName(name) {
|
|
|
356
388
|
return !!name && RE_COMPONENT_NAME.test(name);
|
|
357
389
|
}
|
|
358
390
|
function isClassComponent(node) {
|
|
359
|
-
if (!("superClass" in node && node.superClass)) return false;
|
|
360
|
-
const { superClass } = node;
|
|
361
|
-
return match(superClass).with({
|
|
362
|
-
type: AST_NODE_TYPES.Identifier,
|
|
363
|
-
name: P.string
|
|
364
|
-
}, ({ name }) => /^(?:Pure)?Component$/u.test(name)).with({
|
|
365
|
-
type: AST_NODE_TYPES.MemberExpression,
|
|
366
|
-
property: { name: P.string }
|
|
367
|
-
}, ({ property }) => /^(?:Pure)?Component$/u.test(property.name)).otherwise(() => false);
|
|
368
|
-
}
|
|
369
|
-
function isPureComponent(node) {
|
|
370
391
|
if ("superClass" in node && node.superClass) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
type
|
|
376
|
-
|
|
377
|
-
}
|
|
392
|
+
const re = /^(?:Pure)?Component$/u;
|
|
393
|
+
switch (true) {
|
|
394
|
+
case node.superClass.type === AST_NODE_TYPES.Identifier:
|
|
395
|
+
return re.test(node.superClass.name);
|
|
396
|
+
case (node.superClass.type === AST_NODE_TYPES.MemberExpression && node.superClass.property.type === AST_NODE_TYPES.Identifier):
|
|
397
|
+
return re.test(node.superClass.property.name);
|
|
398
|
+
}
|
|
378
399
|
}
|
|
379
400
|
return false;
|
|
380
401
|
}
|
|
381
|
-
function
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
function isComponentWillUnmountFunction(node) {
|
|
391
|
-
return AST9.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
|
|
392
|
-
}
|
|
393
|
-
function useComponentCollectorLegacy() {
|
|
394
|
-
const components = /* @__PURE__ */ new Map();
|
|
395
|
-
const ctx = {
|
|
396
|
-
getAllComponents(_) {
|
|
397
|
-
return components;
|
|
402
|
+
function isPureComponent(node) {
|
|
403
|
+
if ("superClass" in node && node.superClass) {
|
|
404
|
+
const re = /^PureComponent$/u;
|
|
405
|
+
switch (true) {
|
|
406
|
+
case node.superClass.type === AST_NODE_TYPES.Identifier:
|
|
407
|
+
return re.test(node.superClass.name);
|
|
408
|
+
case (node.superClass.type === AST_NODE_TYPES.MemberExpression && node.superClass.property.type === AST_NODE_TYPES.Identifier):
|
|
409
|
+
return re.test(node.superClass.property.name);
|
|
398
410
|
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (!isClassComponent(node)) return;
|
|
402
|
-
const id2 = AST9.getClassIdentifier(node);
|
|
403
|
-
const key = getId();
|
|
404
|
-
const flag = isPureComponent(node) ? ERClassComponentFlag.PureComponent : ERClassComponentFlag.None;
|
|
405
|
-
components.set(
|
|
406
|
-
key,
|
|
407
|
-
{
|
|
408
|
-
_: key,
|
|
409
|
-
id: id2,
|
|
410
|
-
kind: "class",
|
|
411
|
-
name: O.flatMapNullable(id2, (n) => n.name),
|
|
412
|
-
node,
|
|
413
|
-
// TODO: Get displayName of class component
|
|
414
|
-
displayName: O.none(),
|
|
415
|
-
flag,
|
|
416
|
-
hint: 0n,
|
|
417
|
-
// TODO: Get methods of class component
|
|
418
|
-
methods: []
|
|
419
|
-
}
|
|
420
|
-
);
|
|
421
|
-
};
|
|
422
|
-
const listeners = {
|
|
423
|
-
"ClassDeclaration[type]": collect,
|
|
424
|
-
"ClassExpression[type]": collect
|
|
425
|
-
};
|
|
426
|
-
return { ctx, listeners };
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
427
413
|
}
|
|
428
414
|
|
|
429
415
|
// src/component/component-render-method.ts
|
|
430
|
-
|
|
431
|
-
key
|
|
432
|
-
|
|
433
|
-
name: "render"
|
|
434
|
-
},
|
|
435
|
-
type: P.union(AST_NODE_TYPES.MethodDefinition, AST_NODE_TYPES.PropertyDefinition),
|
|
436
|
-
parent: {
|
|
437
|
-
type: AST_NODE_TYPES.ClassBody,
|
|
438
|
-
parent: {
|
|
439
|
-
type: AST_NODE_TYPES.ClassDeclaration
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
});
|
|
416
|
+
function isRenderMethodLike(node) {
|
|
417
|
+
return (node.type === AST_NODE_TYPES.MethodDefinition || node.type === AST_NODE_TYPES.PropertyDefinition) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "render" && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration;
|
|
418
|
+
}
|
|
443
419
|
function isFunctionOfRenderMethod(node) {
|
|
444
|
-
if (!isRenderMethodLike(node.parent))
|
|
420
|
+
if (!isRenderMethodLike(node.parent)) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
445
423
|
return isClassComponent(node.parent.parent.parent);
|
|
446
424
|
}
|
|
447
425
|
function isInsideRenderMethod(node) {
|
|
448
|
-
return O.isSome(
|
|
449
|
-
return isRenderMethodLike(node2) && isClassComponent(node2.parent.parent);
|
|
450
|
-
}));
|
|
426
|
+
return O.isSome(AST8.findParentNode(node, (node2) => isRenderMethodLike(node2) && isClassComponent(node2.parent.parent)));
|
|
451
427
|
}
|
|
452
428
|
function hasNoneOrValidComponentName(node, context) {
|
|
453
429
|
const mbIdentifier = getFunctionComponentIdentifier(node, context);
|
|
@@ -464,22 +440,22 @@ function hasValidHierarchy(node, context, hint) {
|
|
|
464
440
|
if (isChildrenOfCreateElement(node, context) || isFunctionOfRenderMethod(node)) {
|
|
465
441
|
return false;
|
|
466
442
|
}
|
|
467
|
-
if (hint & ERComponentHint.SkipMapCallback &&
|
|
443
|
+
if (hint & ERComponentHint.SkipMapCallback && AST8.isMapCallLoose(node.parent)) {
|
|
468
444
|
return false;
|
|
469
445
|
}
|
|
470
|
-
if (hint & ERComponentHint.SkipObjectMethod &&
|
|
446
|
+
if (hint & ERComponentHint.SkipObjectMethod && AST8.isFunctionOfObjectMethod(node.parent)) {
|
|
471
447
|
return false;
|
|
472
448
|
}
|
|
473
|
-
if (hint & ERComponentHint.SkipClassMethod &&
|
|
449
|
+
if (hint & ERComponentHint.SkipClassMethod && AST8.isFunctionOfClassMethod(node.parent)) {
|
|
474
450
|
return false;
|
|
475
451
|
}
|
|
476
|
-
if (hint & ERComponentHint.SkipClassProperty &&
|
|
452
|
+
if (hint & ERComponentHint.SkipClassProperty && AST8.isFunctionOfClassProperty(node.parent)) {
|
|
477
453
|
return false;
|
|
478
454
|
}
|
|
479
455
|
return !O.exists(
|
|
480
|
-
|
|
456
|
+
AST8.findParentNode(
|
|
481
457
|
node,
|
|
482
|
-
|
|
458
|
+
AST8.isOneOf([
|
|
483
459
|
AST_NODE_TYPES.JSXExpressionContainer,
|
|
484
460
|
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
485
461
|
AST_NODE_TYPES.FunctionExpression,
|
|
@@ -487,15 +463,15 @@ function hasValidHierarchy(node, context, hint) {
|
|
|
487
463
|
AST_NODE_TYPES.ClassBody
|
|
488
464
|
])
|
|
489
465
|
),
|
|
490
|
-
|
|
466
|
+
AST8.is(AST_NODE_TYPES.JSXExpressionContainer)
|
|
491
467
|
);
|
|
492
468
|
}
|
|
493
469
|
function getComponentFlag(initPath) {
|
|
494
470
|
let flag = ERFunctionComponentFlag.None;
|
|
495
|
-
if (
|
|
471
|
+
if (AST8.hasCallInFunctionInitPath("memo")(initPath)) {
|
|
496
472
|
flag |= ERFunctionComponentFlag.Memo;
|
|
497
473
|
}
|
|
498
|
-
if (
|
|
474
|
+
if (AST8.hasCallInFunctionInitPath("forwardRef")(initPath)) {
|
|
499
475
|
flag |= ERFunctionComponentFlag.ForwardRef;
|
|
500
476
|
}
|
|
501
477
|
return flag;
|
|
@@ -511,11 +487,15 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
511
487
|
};
|
|
512
488
|
const onFunctionExit = () => {
|
|
513
489
|
const { key, node, isComponent } = functionEntries.at(-1) ?? {};
|
|
514
|
-
if (!key || !node || !isComponent)
|
|
515
|
-
|
|
490
|
+
if (!key || !node || !isComponent) {
|
|
491
|
+
return functionEntries.pop();
|
|
492
|
+
}
|
|
493
|
+
const shouldDrop = AST8.getNestedReturnStatements(node.body).slice().reverse().some((r) => {
|
|
516
494
|
return context.sourceCode.getScope(r).block === node && r.argument !== null && !JSX4.isJSXValue(r.argument, jsxCtx, hint);
|
|
517
495
|
});
|
|
518
|
-
if (shouldDrop)
|
|
496
|
+
if (shouldDrop) {
|
|
497
|
+
components.delete(key);
|
|
498
|
+
}
|
|
519
499
|
return functionEntries.pop();
|
|
520
500
|
};
|
|
521
501
|
const ctx = {
|
|
@@ -532,12 +512,16 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
532
512
|
":function[type]:exit": onFunctionExit,
|
|
533
513
|
"ArrowFunctionExpression[type][body.type!='BlockStatement']"() {
|
|
534
514
|
const mbEntry = getCurrentFunction();
|
|
535
|
-
if (O.isNone(mbEntry))
|
|
515
|
+
if (O.isNone(mbEntry)) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
536
518
|
const entry = mbEntry.value;
|
|
537
519
|
const { body } = entry.node;
|
|
538
520
|
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX4.isJSXValue(body, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
539
|
-
if (!isComponent)
|
|
540
|
-
|
|
521
|
+
if (!isComponent) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
541
525
|
const id2 = getFunctionComponentIdentifier(entry.node, context);
|
|
542
526
|
const name = O.flatMapNullable(id2, getComponentNameFromIdentifier);
|
|
543
527
|
const key = getId();
|
|
@@ -557,32 +541,44 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
557
541
|
"AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']"(node) {
|
|
558
542
|
const { left, right } = node;
|
|
559
543
|
const mbComponentName = match(left.object).with({ type: AST_NODE_TYPES.Identifier }, (n) => O.some(n.name)).otherwise(O.none);
|
|
560
|
-
if (O.isNone(mbComponentName))
|
|
544
|
+
if (O.isNone(mbComponentName)) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
561
547
|
const componentName = mbComponentName.value;
|
|
562
548
|
const component = Array.from(components.values()).findLast(({ name }) => O.exists(name, (n) => n === componentName));
|
|
563
|
-
if (!component)
|
|
549
|
+
if (!component) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
564
552
|
components.set(component._, {
|
|
565
553
|
...component,
|
|
566
554
|
displayName: O.some(right)
|
|
567
555
|
});
|
|
568
556
|
},
|
|
569
557
|
"CallExpression[type]:exit"(node) {
|
|
570
|
-
if (!isReactHookCall(node))
|
|
558
|
+
if (!isReactHookCall(node)) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
571
561
|
const mbEntry = getCurrentFunction();
|
|
572
|
-
if (O.isNone(mbEntry))
|
|
562
|
+
if (O.isNone(mbEntry)) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
573
565
|
const entry = mbEntry.value;
|
|
574
566
|
functionEntries.pop();
|
|
575
567
|
functionEntries.push({ ...entry, hookCalls: [...entry.hookCalls, node] });
|
|
576
568
|
},
|
|
577
569
|
"ReturnStatement[type]"(node) {
|
|
578
570
|
const mbEntry = getCurrentFunction();
|
|
579
|
-
if (O.isNone(mbEntry))
|
|
571
|
+
if (O.isNone(mbEntry)) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
580
574
|
const entry = mbEntry.value;
|
|
581
575
|
const isComponent = hasNoneOrValidComponentName(entry.node, context) && JSX4.isJSXValue(node.argument, jsxCtx, hint) && hasValidHierarchy(entry.node, context, hint);
|
|
582
|
-
if (!isComponent)
|
|
576
|
+
if (!isComponent) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
583
579
|
functionEntries.pop();
|
|
584
580
|
functionEntries.push({ ...entry, isComponent });
|
|
585
|
-
const initPath =
|
|
581
|
+
const initPath = AST8.getFunctionInitPath(entry.node);
|
|
586
582
|
const id2 = getFunctionComponentIdentifier(entry.node, context);
|
|
587
583
|
const name = O.flatMapNullable(id2, getComponentNameFromIdentifier);
|
|
588
584
|
components.set(entry.key, {
|
|
@@ -601,17 +597,70 @@ function useComponentCollector(context, hint = DEFAULT_COMPONENT_HINT) {
|
|
|
601
597
|
};
|
|
602
598
|
return { ctx, listeners };
|
|
603
599
|
}
|
|
600
|
+
function useComponentCollectorLegacy() {
|
|
601
|
+
const components = /* @__PURE__ */ new Map();
|
|
602
|
+
const ctx = {
|
|
603
|
+
getAllComponents(_) {
|
|
604
|
+
return components;
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
const collect = (node) => {
|
|
608
|
+
if (!isClassComponent(node)) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const id2 = AST8.getClassIdentifier(node);
|
|
612
|
+
const key = getId();
|
|
613
|
+
const flag = isPureComponent(node) ? ERClassComponentFlag.PureComponent : ERClassComponentFlag.None;
|
|
614
|
+
components.set(
|
|
615
|
+
key,
|
|
616
|
+
{
|
|
617
|
+
_: key,
|
|
618
|
+
id: id2,
|
|
619
|
+
kind: "class",
|
|
620
|
+
name: O.flatMapNullable(id2, (n) => n.name),
|
|
621
|
+
node,
|
|
622
|
+
// TODO: Get displayName of class component
|
|
623
|
+
displayName: O.none(),
|
|
624
|
+
flag,
|
|
625
|
+
hint: 0n,
|
|
626
|
+
// TODO: Get methods of class component
|
|
627
|
+
methods: []
|
|
628
|
+
}
|
|
629
|
+
);
|
|
630
|
+
};
|
|
631
|
+
const listeners = {
|
|
632
|
+
"ClassDeclaration[type]": collect,
|
|
633
|
+
"ClassExpression[type]": collect
|
|
634
|
+
};
|
|
635
|
+
return { ctx, listeners };
|
|
636
|
+
}
|
|
604
637
|
function isSetupFunction(node) {
|
|
605
638
|
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);
|
|
606
639
|
}
|
|
607
640
|
function isCleanupFunction(node) {
|
|
608
|
-
const nearestRet = O.getOrNull(
|
|
609
|
-
if (!nearestRet)
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
641
|
+
const nearestRet = O.getOrNull(AST8.findParentNodeGuard(node, AST8.is(AST_NODE_TYPES.ReturnStatement)));
|
|
642
|
+
if (!nearestRet) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
const nearestFunction = O.getOrNull(AST8.findParentNodeGuard(node, AST8.isFunction));
|
|
646
|
+
const nearestFunctionOfRet = O.getOrNull(AST8.findParentNodeGuard(nearestRet, AST8.isFunction));
|
|
647
|
+
if (!nearestFunction || !nearestFunctionOfRet) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
613
650
|
return nearestFunction === nearestFunctionOfRet && isSetupFunction(nearestFunction);
|
|
614
651
|
}
|
|
652
|
+
function isComponentDidMount(node) {
|
|
653
|
+
return AST8.isOneOf([AST_NODE_TYPES.MethodDefinition, AST_NODE_TYPES.PropertyDefinition])(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentDidMount";
|
|
654
|
+
}
|
|
655
|
+
function isComponentWillUnmount(node) {
|
|
656
|
+
return AST8.isOneOf([AST_NODE_TYPES.MethodDefinition, AST_NODE_TYPES.PropertyDefinition])(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillUnmount";
|
|
657
|
+
}
|
|
658
|
+
function isComponentDidMountFunction(node) {
|
|
659
|
+
return AST8.isFunction(node) && isComponentDidMount(node.parent) && node.parent.value === node;
|
|
660
|
+
}
|
|
661
|
+
function isComponentWillUnmountFunction(node) {
|
|
662
|
+
return AST8.isFunction(node) && isComponentWillUnmount(node.parent) && node.parent.value === node;
|
|
663
|
+
}
|
|
615
664
|
var ERPhaseRelevance = birecord({
|
|
616
665
|
mount: "unmount",
|
|
617
666
|
setup: "cleanup"
|
|
@@ -632,9 +681,9 @@ function isDeclaredInRenderPropLoose(node) {
|
|
|
632
681
|
return true;
|
|
633
682
|
}
|
|
634
683
|
return F.pipe(
|
|
635
|
-
|
|
684
|
+
AST8.findParentNodeGuard(node, AST8.is(AST_NODE_TYPES.JSXExpressionContainer)),
|
|
636
685
|
O.flatMapNullable((c) => c.parent),
|
|
637
|
-
O.filter(
|
|
686
|
+
O.filter(AST8.is(AST_NODE_TYPES.JSXAttribute)),
|
|
638
687
|
O.flatMapNullable((a) => a.name),
|
|
639
688
|
O.exists(isMatching({
|
|
640
689
|
type: AST_NODE_TYPES.JSXIdentifier,
|
|
@@ -644,7 +693,7 @@ function isDeclaredInRenderPropLoose(node) {
|
|
|
644
693
|
}
|
|
645
694
|
function isRenderFunctionLoose(node, context) {
|
|
646
695
|
const { body, parent } = node;
|
|
647
|
-
if (O.exists(
|
|
696
|
+
if (O.exists(AST8.getFunctionIdentifier(node), (id2) => id2.name.startsWith("render"))) {
|
|
648
697
|
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");
|
|
649
698
|
}
|
|
650
699
|
return JSX4.isJSXValue(
|
|
@@ -652,16 +701,18 @@ function isRenderFunctionLoose(node, context) {
|
|
|
652
701
|
{
|
|
653
702
|
getScope: (node2) => context.sourceCode.getScope(node2)
|
|
654
703
|
},
|
|
655
|
-
JSX4.JSXValueHint.SkipNullLiteral | JSX4.JSXValueHint.
|
|
704
|
+
JSX4.JSXValueHint.SkipNullLiteral | JSX4.JSXValueHint.SkipUndefined | JSX4.JSXValueHint.StrictLogical | JSX4.JSXValueHint.StrictConditional
|
|
656
705
|
);
|
|
657
706
|
}
|
|
658
707
|
function isRenderPropLoose(node, context) {
|
|
659
|
-
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier)
|
|
660
|
-
|
|
708
|
+
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
return node.name.name.startsWith("render") && node.value?.type === AST_NODE_TYPES.JSXExpressionContainer && AST8.isFunction(node.value.expression) && isRenderFunctionLoose(node.value.expression, context);
|
|
661
712
|
}
|
|
662
713
|
function isThisSetState(node) {
|
|
663
714
|
const { callee } = node;
|
|
664
|
-
return callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
715
|
+
return callee.type === AST_NODE_TYPES.MemberExpression && AST8.isThisExpression(callee.object) && callee.property.type === AST_NODE_TYPES.Identifier && callee.property.name === "setState";
|
|
665
716
|
}
|
|
666
717
|
|
|
667
|
-
export { DEFAULT_COMPONENT_HINT, ERClassComponentFlag, ERComponentHint, ERFunctionComponentFlag, ERPhaseRelevance, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier, getElementRepresentName, getFunctionComponentIdentifier, getId, hasNoneOrValidComponentName, isCallFromReact, isCallFromReactMember, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCleanupFunction, isCloneElement, isCloneElementCall, isComponentDidMount, isComponentDidMountFunction, isComponentName, isComponentWillUnmount, isComponentWillUnmountFunction, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFromReact, isFromReactMember, isFunctionOfRenderMethod, isInitializedFromReact, isInsideCreateElementProps, isInsideReactHook, isInsideRenderMethod, isInversePhase, isMemo, isMemoCall, isPureComponent, isReactAPICallWithName, isReactAPIWithName, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookName, isRenderFunctionLoose, isRenderPropLoose, isSetupFunction, isThisSetState, isUseCallbackCall, isUseContextCall, isUseDebugValueCall, isUseDeferredValueCall, isUseEffectCall, isUseEffectCallLoose, isUseIdCall, isUseImperativeHandleCall, isUseInsertionEffectCall, isUseLayoutEffectCall, isUseMemoCall, isUseReducerCall, isUseRefCall, isUseStateCall, isUseSyncExternalStoreCall, isUseTransitionCall, useComponentCollector, useComponentCollectorLegacy, useHookCollector };
|
|
718
|
+
export { DEFAULT_COMPONENT_HINT, ERClassComponentFlag, ERComponentHint, ERFunctionComponentFlag, ERPhaseRelevance, RE_COMPONENT_NAME, RE_HOOK_NAME, getComponentNameFromIdentifier, getElementRepresentName, getFunctionComponentIdentifier, getId, hasNoneOrValidComponentName, isCallFromReact, isCallFromReactMember, isChildrenCount, isChildrenCountCall, isChildrenForEach, isChildrenForEachCall, isChildrenMap, isChildrenMapCall, isChildrenOfCreateElement, isChildrenOnly, isChildrenOnlyCall, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCleanupFunction, isCloneElement, isCloneElementCall, isComponentDidMount, isComponentDidMountFunction, isComponentName, isComponentWillUnmount, isComponentWillUnmountFunction, isCreateContext, isCreateContextCall, isCreateElement, isCreateElementCall, isCreateRef, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRef, isForwardRefCall, isFromReact, isFromReactMember, isFunctionOfRenderMethod, isInitializedFromReact, isInsideCreateElementProps, isInsideReactHook, isInsideRenderMethod, isInversePhase, isMemo, isMemoCall, isPureComponent, isReactAPICallWithName, isReactAPIWithName, isReactHook, isReactHookCall, isReactHookCallWithName, isReactHookCallWithNameAlias, isReactHookCallWithNameLoose, isReactHookName, isRenderFunctionLoose, isRenderMethodLike, isRenderPropLoose, isSetupFunction, 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.23.2",
|
|
3
|
+
"version": "1.23.3-beta.2",
|
|
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": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"author": "Eva1ent<rel1cx@proton.me>",
|
|
16
|
+
"sideEffects": false,
|
|
16
17
|
"exports": {
|
|
17
18
|
".": {
|
|
18
19
|
"import": {
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
"./package.json": "./package.json"
|
|
28
29
|
},
|
|
29
30
|
"main": "dist/index.js",
|
|
31
|
+
"module": "dist/index.mjs",
|
|
30
32
|
"types": "dist/index.d.ts",
|
|
31
33
|
"files": [
|
|
32
34
|
"dist",
|
|
@@ -38,12 +40,12 @@
|
|
|
38
40
|
"@typescript-eslint/types": "^8.19.1",
|
|
39
41
|
"@typescript-eslint/utils": "^8.19.1",
|
|
40
42
|
"ts-pattern": "^5.6.0",
|
|
41
|
-
"@eslint-react/ast": "1.23.2",
|
|
42
|
-
"@eslint-react/
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/
|
|
43
|
+
"@eslint-react/ast": "1.23.3-beta.2",
|
|
44
|
+
"@eslint-react/jsx": "1.23.3-beta.2",
|
|
45
|
+
"@eslint-react/eff": "1.23.3-beta.2",
|
|
46
|
+
"@eslint-react/types": "1.23.3-beta.2",
|
|
47
|
+
"@eslint-react/var": "1.23.3-beta.2",
|
|
48
|
+
"@eslint-react/shared": "1.23.3-beta.2"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"tsup": "^8.3.5",
|
|
@@ -53,8 +55,6 @@
|
|
|
53
55
|
"bun": ">=1.0.15",
|
|
54
56
|
"node": ">=18.18.0"
|
|
55
57
|
},
|
|
56
|
-
"sideEffects": false,
|
|
57
|
-
"module": "dist/index.mjs",
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup",
|
|
60
60
|
"build:docs": "typedoc",
|