eslint-plugin-react-x 5.18.1 → 5.18.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.js +95 -17
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -145,7 +145,7 @@ const rules$6 = {
|
|
|
145
145
|
//#endregion
|
|
146
146
|
//#region package.json
|
|
147
147
|
var name$6 = "eslint-plugin-react-x";
|
|
148
|
-
var version = "5.18.
|
|
148
|
+
var version = "5.18.2";
|
|
149
149
|
|
|
150
150
|
//#endregion
|
|
151
151
|
//#region src/utils/create-rule.ts
|
|
@@ -3397,21 +3397,79 @@ function create$23(context) {
|
|
|
3397
3397
|
//#endregion
|
|
3398
3398
|
//#region src/rules/no-nested-component-definitions/lib.ts
|
|
3399
3399
|
/**
|
|
3400
|
-
*
|
|
3401
|
-
*
|
|
3402
|
-
*
|
|
3400
|
+
* Well-known component wrapper function names, matched by exact or `.`-suffixed fully
|
|
3401
|
+
* qualified name (e.g. `memo`, `React.memo`, `mobx.observer`, `connect` from react-redux,
|
|
3402
|
+
* or Relay's `create*Container` helpers). Only wrappers whose argument is a render function
|
|
3403
|
+
* are listed; wrappers taking a component identifier (`styled`, `motion`) or a loader
|
|
3404
|
+
* (`lazy`, `dynamic`) are irrelevant for name resolution.
|
|
3403
3405
|
*/
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
+
const WELL_KNOWN_COMPONENT_WRAPPERS = [
|
|
3407
|
+
"connect",
|
|
3408
|
+
"createFragmentContainer",
|
|
3409
|
+
"createPaginationContainer",
|
|
3410
|
+
"createRefetchContainer",
|
|
3411
|
+
"forwardRef",
|
|
3412
|
+
"graphql",
|
|
3413
|
+
"memo",
|
|
3414
|
+
"observer",
|
|
3415
|
+
"useCallback"
|
|
3416
|
+
];
|
|
3417
|
+
/** Matches the HOC naming convention shared by recompose, react-router v5, Formik and custom HOCs (e.g. `withProps`, `withRouter`, `withFormik`, `withAuth`). */
|
|
3418
|
+
const RE_HOC_WRAPPER_NAME = /^with[A-Z]/;
|
|
3419
|
+
/**
|
|
3420
|
+
* Check if a call expression is a well-known component wrapper call.
|
|
3421
|
+
* Only calls whose callee's fully qualified name is (or ends with) a well-known wrapper
|
|
3422
|
+
* name (e.g. `memo`, `React.memo`, `React.useCallback`, `mobx.observer`) are treated as
|
|
3423
|
+
* wrappers; anything else, including member calls on data objects (e.g. `items.map`,
|
|
3424
|
+
* `Array.from`), is not, so array method callbacks are never mistaken for wrapped components.
|
|
3425
|
+
* Curried wrappers like `connect(...)(Component)` or `withFormik(...)(Component)` are
|
|
3426
|
+
* recognized by unwrapping nested callee call expressions.
|
|
3427
|
+
* @param context The rule context
|
|
3428
|
+
* @param node The call expression to check
|
|
3429
|
+
* @returns `true` if the call is a well-known component wrapper call
|
|
3430
|
+
*/
|
|
3431
|
+
function isWellKnownComponentWrapperCall(context, node) {
|
|
3432
|
+
let callee = Extract.unwrap(node.callee);
|
|
3433
|
+
while (callee.type === AST_NODE_TYPES.CallExpression) callee = Extract.unwrap(callee.callee);
|
|
3434
|
+
const name = Extract.getFullyQualifiedName(callee, (n) => context.sourceCode.getText(n));
|
|
3435
|
+
const baseName = name.slice(name.lastIndexOf(".") + 1);
|
|
3436
|
+
return RE_HOC_WRAPPER_NAME.test(baseName) || WELL_KNOWN_COMPONENT_WRAPPERS.some((wrapper) => name === wrapper || name.endsWith(`.${wrapper}`));
|
|
3406
3437
|
}
|
|
3407
3438
|
/**
|
|
3408
|
-
* Check
|
|
3409
|
-
*
|
|
3410
|
-
* @param
|
|
3411
|
-
* @
|
|
3439
|
+
* Check if a call expression is a component wrapper call for name resolution purposes.
|
|
3440
|
+
* @param context The rule context
|
|
3441
|
+
* @param call The call expression to check
|
|
3442
|
+
* @param arg The function node passed to the call
|
|
3443
|
+
* @returns `true` if the call is a component wrapper call
|
|
3412
3444
|
*/
|
|
3413
|
-
function
|
|
3414
|
-
|
|
3445
|
+
function isComponentWrapperCall(context, call, arg) {
|
|
3446
|
+
if (Extract.unwrap(call.callee) === arg) return false;
|
|
3447
|
+
return isWellKnownComponentWrapperCall(context, call);
|
|
3448
|
+
}
|
|
3449
|
+
/**
|
|
3450
|
+
* Resolve the name a function is bound to through a chain of wrapping call expressions,
|
|
3451
|
+
* e.g. `const Component = useCallback(() => <div />, [])` resolves to `Component`.
|
|
3452
|
+
* @param context The rule context
|
|
3453
|
+
* @param node The function node to resolve the bound name for
|
|
3454
|
+
* @returns The bound name if the call chain ends at a variable declarator with an identifier, `null` otherwise
|
|
3455
|
+
*/
|
|
3456
|
+
function getWrapperCallBoundName(context, node) {
|
|
3457
|
+
let current = node;
|
|
3458
|
+
let parent = current.parent;
|
|
3459
|
+
while (Check.isTypeExpression(parent)) {
|
|
3460
|
+
current = parent;
|
|
3461
|
+
parent = parent.parent;
|
|
3462
|
+
}
|
|
3463
|
+
while (parent.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCall(context, parent, current)) {
|
|
3464
|
+
current = parent;
|
|
3465
|
+
parent = parent.parent;
|
|
3466
|
+
while (Check.isTypeExpression(parent)) {
|
|
3467
|
+
current = parent;
|
|
3468
|
+
parent = parent.parent;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
if (parent.type !== AST_NODE_TYPES.VariableDeclarator || parent.id.type !== AST_NODE_TYPES.Identifier) return null;
|
|
3472
|
+
return parent.id.name;
|
|
3415
3473
|
}
|
|
3416
3474
|
/**
|
|
3417
3475
|
* Determine whether the node is inside `createElement`'s props argument
|
|
@@ -3426,6 +3484,23 @@ function isInsideCreateElementProps(context, node) {
|
|
|
3426
3484
|
if (prop == null) return false;
|
|
3427
3485
|
return prop === call.arguments[1];
|
|
3428
3486
|
}
|
|
3487
|
+
/**
|
|
3488
|
+
* Determine whether the node is inside JSX attribute value
|
|
3489
|
+
* @param node The AST node to check
|
|
3490
|
+
* @returns `true` if the node is inside JSX attribute value
|
|
3491
|
+
*/
|
|
3492
|
+
function isInsideJSXAttributeValue(node) {
|
|
3493
|
+
return node.parent.type === AST_NODE_TYPES.JSXAttribute || findParentAttribute(node, (n) => n.value?.type === AST_NODE_TYPES.JSXExpressionContainer) != null;
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Check whether a given node is declared inside a class component's render block
|
|
3497
|
+
* Ex: class C extends React.Component { render() { const Nested = () => <div />; } }
|
|
3498
|
+
* @param node The AST node being checked
|
|
3499
|
+
* @returns `true` if the node is inside a class component's render block
|
|
3500
|
+
*/
|
|
3501
|
+
function isInsideRenderMethod(node) {
|
|
3502
|
+
return Traverse.findParent(node, (n) => core.isRenderMethodLike(n) && core.isClassComponent(n.parent.parent)) != null;
|
|
3503
|
+
}
|
|
3429
3504
|
|
|
3430
3505
|
//#endregion
|
|
3431
3506
|
//#region src/rules/no-nested-component-definitions/no-nested-component-definitions.ts
|
|
@@ -3442,21 +3517,24 @@ var no_nested_component_definitions_default = createRule({
|
|
|
3442
3517
|
defaultOptions: []
|
|
3443
3518
|
});
|
|
3444
3519
|
function create$22(context) {
|
|
3445
|
-
const hint = core.FunctionComponentDetectionHint.DoNotIncludeJsxWithNumberValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithNullValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithStringValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | core.FunctionComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx | core.FunctionComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayPatternElement | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayExpressionElement | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback;
|
|
3520
|
+
const hint = core.FunctionComponentDetectionHint.DoNotIncludeJsxWithNumberValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithNullValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithStringValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | core.FunctionComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx | core.FunctionComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayPatternElement | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayExpressionElement | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayFlatMapCallback;
|
|
3446
3521
|
const fc = core.getFunctionComponentCollector(context, { hint });
|
|
3447
3522
|
const cc = core.getClassComponentCollector(context);
|
|
3448
3523
|
return merge(fc.visitor, cc.visitor, { "Program:exit"(program) {
|
|
3449
3524
|
const fComponents = [...fc.api.getAllComponents(program)];
|
|
3450
3525
|
const cComponents = [...cc.api.getAllComponents(program)];
|
|
3526
|
+
const fComponentNodes = new Set(fComponents.map((c) => c.node));
|
|
3527
|
+
const cComponentNodes = new Set(cComponents.map((c) => c.node));
|
|
3451
3528
|
function findEnclosingComponent(node) {
|
|
3452
3529
|
return Traverse.findParent(node, (n) => {
|
|
3453
|
-
if (Check.isFunction(n)) return
|
|
3454
|
-
if (Check.isClass(n)) return
|
|
3530
|
+
if (Check.isFunction(n)) return fComponentNodes.has(n);
|
|
3531
|
+
if (Check.isClass(n)) return cComponentNodes.has(n);
|
|
3455
3532
|
return false;
|
|
3456
3533
|
});
|
|
3457
3534
|
}
|
|
3458
|
-
for (
|
|
3459
|
-
|
|
3535
|
+
for (let { name, node: component } of fComponents) {
|
|
3536
|
+
name ??= getWrapperCallBoundName(context, component);
|
|
3537
|
+
if (name == null || !core.isFunctionComponentNameLoose(name)) continue;
|
|
3460
3538
|
if (isInsideJSXAttributeValue(component)) {
|
|
3461
3539
|
context.report({
|
|
3462
3540
|
data: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "5.18.
|
|
3
|
+
"version": "5.18.2",
|
|
4
4
|
"description": "A set of composable ESLint rules for libraries and frameworks that use React as a UI runtime.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
"string-ts": "^2.3.1",
|
|
46
46
|
"ts-api-utils": "^2.5.0",
|
|
47
47
|
"ts-pattern": "^5.9.0",
|
|
48
|
-
"@eslint-react/ast": "5.18.
|
|
49
|
-
"@eslint-react/core": "5.18.
|
|
50
|
-
"@eslint-react/
|
|
51
|
-
"@eslint-react/
|
|
52
|
-
"@eslint-react/
|
|
53
|
-
"@eslint-react/
|
|
48
|
+
"@eslint-react/ast": "5.18.2",
|
|
49
|
+
"@eslint-react/core": "5.18.2",
|
|
50
|
+
"@eslint-react/eslint": "5.18.2",
|
|
51
|
+
"@eslint-react/jsx": "5.18.2",
|
|
52
|
+
"@eslint-react/var": "5.18.2",
|
|
53
|
+
"@eslint-react/shared": "5.18.2"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@types/react": "^19.2.
|
|
57
|
-
"@types/react-dom": "^19.2.
|
|
56
|
+
"@types/react": "^19.2.18",
|
|
57
|
+
"@types/react-dom": "^19.2.4",
|
|
58
58
|
"dedent": "^1.7.2",
|
|
59
59
|
"eslint": "^10.8.0",
|
|
60
60
|
"react": "^19.2.8",
|