eslint-plugin-react-x 2.7.4 → 2.7.5-beta.11
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 +34 -53
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
|
3
3
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
4
4
|
import { P, isMatching, match } from "ts-pattern";
|
|
5
5
|
import ts from "typescript";
|
|
6
|
-
import { ComponentDetectionHint, ComponentFlag, DEFAULT_COMPONENT_DETECTION_HINT, JsxEmit, findParentJsxAttribute, getJsxAttribute, getJsxAttributeName, getJsxConfigFromAnnotation, getJsxConfigFromContext, getJsxElementType, isAssignmentToThisState, isCaptureOwnerStackCall, isChildrenCount, isChildrenForEach, isChildrenMap, isChildrenOnly, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUpdate, isCreateContextCall, isCreateElementCall, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRefCall, isGetDerivedStateFromError, isGetDerivedStateFromProps, isInitializedFromReact, isJsxFragmentElement, isJsxHostElement, isJsxText, isLazyCall,
|
|
6
|
+
import { ComponentDetectionHint, ComponentFlag, DEFAULT_COMPONENT_DETECTION_HINT, JsxEmit, findParentJsxAttribute, getJsxAttribute, getJsxAttributeName, getJsxConfigFromAnnotation, getJsxConfigFromContext, getJsxElementType, isAssignmentToThisState, isCaptureOwnerStackCall, isChildrenCount, isChildrenForEach, isChildrenMap, isChildrenOnly, isChildrenToArray, isChildrenToArrayCall, isClassComponent, isCloneElementCall, isComponentDidCatch, isComponentDidMount, isComponentDidUpdate, isComponentNameLoose, isComponentWillMount, isComponentWillReceiveProps, isComponentWillUpdate, isCreateContextCall, isCreateElementCall, isCreateRefCall, isDeclaredInRenderPropLoose, isDirectValueOfRenderPropertyLoose, isForwardRefCall, isGetDerivedStateFromError, isGetDerivedStateFromProps, isHookCall, isHookName, isInitializedFromReact, isJsxFragmentElement, isJsxHostElement, isJsxText, isLazyCall, isRenderFunctionLoose, isRenderMethodLike, isThisSetState, isUnsafeComponentWillMount, isUnsafeComponentWillReceiveProps, isUnsafeComponentWillUpdate, isUseCall, isUseCallbackCall, isUseContextCall, isUseEffectLikeCall, isUseMemoCall, isUseRefCall, isUseStateCall, useComponentCollector, useComponentCollectorLegacy, useHookCollector } from "@eslint-react/core";
|
|
7
7
|
import * as AST from "@eslint-react/ast";
|
|
8
8
|
import { findEnclosingAssignmentTarget, findVariable, getChildScopes, getObjectType, getVariableDefinitionNode, isAssignmentTargetEqual } from "@eslint-react/var";
|
|
9
9
|
import { constFalse, constTrue, flow, getOrElseUpdate, identity, unit } from "@eslint-react/eff";
|
|
@@ -68,7 +68,7 @@ const rules$7 = {
|
|
|
68
68
|
//#endregion
|
|
69
69
|
//#region package.json
|
|
70
70
|
var name$6 = "eslint-plugin-react-x";
|
|
71
|
-
var version = "2.7.
|
|
71
|
+
var version = "2.7.5-beta.11";
|
|
72
72
|
|
|
73
73
|
//#endregion
|
|
74
74
|
//#region src/utils/create-rule.ts
|
|
@@ -1294,7 +1294,7 @@ function create$35(context) {
|
|
|
1294
1294
|
} };
|
|
1295
1295
|
}
|
|
1296
1296
|
/**
|
|
1297
|
-
*
|
|
1297
|
+
* Determine whether the given CallExpression can be safely auto-fixed by replacing
|
|
1298
1298
|
* the usage of `forwardRef` with passing `ref` as a prop
|
|
1299
1299
|
*
|
|
1300
1300
|
* @param context The rule context object
|
|
@@ -1471,7 +1471,7 @@ const RULE_NAME$32 = "no-missing-component-display-name";
|
|
|
1471
1471
|
var no_missing_component_display_name_default = createRule({
|
|
1472
1472
|
meta: {
|
|
1473
1473
|
type: "problem",
|
|
1474
|
-
docs: { description: "Enforces that all components have a 'displayName' that can be used in
|
|
1474
|
+
docs: { description: "Enforces that all components have a 'displayName' that can be used in DevTools." },
|
|
1475
1475
|
messages: { noMissingComponentDisplayName: "Add missing 'displayName' for component." },
|
|
1476
1476
|
schema: []
|
|
1477
1477
|
},
|
|
@@ -1483,7 +1483,6 @@ function create$32(context) {
|
|
|
1483
1483
|
if (!context.sourceCode.text.includes("memo") && !context.sourceCode.text.includes("forwardRef")) return {};
|
|
1484
1484
|
const { ctx, visitor } = useComponentCollector(context, {
|
|
1485
1485
|
collectDisplayName: true,
|
|
1486
|
-
collectHookCalls: false,
|
|
1487
1486
|
hint: DEFAULT_COMPONENT_DETECTION_HINT
|
|
1488
1487
|
});
|
|
1489
1488
|
return defineRuleListener(visitor, { "Program:exit"(program) {
|
|
@@ -1506,7 +1505,7 @@ const RULE_NAME$31 = "no-missing-context-display-name";
|
|
|
1506
1505
|
var no_missing_context_display_name_default = createRule({
|
|
1507
1506
|
meta: {
|
|
1508
1507
|
type: "problem",
|
|
1509
|
-
docs: { description: "Enforces that all contexts have a 'displayName' that can be used in
|
|
1508
|
+
docs: { description: "Enforces that all contexts have a 'displayName' that can be used in DevTools." },
|
|
1510
1509
|
fixable: "code",
|
|
1511
1510
|
messages: { noMissingContextDisplayName: "Add missing 'displayName' for context." },
|
|
1512
1511
|
schema: []
|
|
@@ -1776,7 +1775,7 @@ function create$28(context) {
|
|
|
1776
1775
|
} });
|
|
1777
1776
|
}
|
|
1778
1777
|
/**
|
|
1779
|
-
*
|
|
1778
|
+
* Determine whether the node is inside JSX attribute value
|
|
1780
1779
|
* @param node The AST node to check
|
|
1781
1780
|
* @returns `true` if the node is inside JSX attribute value
|
|
1782
1781
|
*/
|
|
@@ -1784,7 +1783,7 @@ function isInsideJSXAttributeValue(node) {
|
|
|
1784
1783
|
return node.parent.type === AST_NODE_TYPES.JSXAttribute || findParentJsxAttribute(node, (n) => n.value?.type === AST_NODE_TYPES.JSXExpressionContainer) != null;
|
|
1785
1784
|
}
|
|
1786
1785
|
/**
|
|
1787
|
-
*
|
|
1786
|
+
* Check whether a given node is declared inside a class component's render block
|
|
1788
1787
|
* Ex: class C extends React.Component { render() { const Nested = () => <div />; } }
|
|
1789
1788
|
* @param node The AST node being checked
|
|
1790
1789
|
* @returns `true` if the node is inside a class component's render block
|
|
@@ -1793,7 +1792,7 @@ function isInsideRenderMethod(node) {
|
|
|
1793
1792
|
return AST.findParentNode(node, (n) => isRenderMethodLike(n) && isClassComponent(n.parent.parent)) != null;
|
|
1794
1793
|
}
|
|
1795
1794
|
/**
|
|
1796
|
-
*
|
|
1795
|
+
* Determine whether the node is inside `createElement`'s props argument
|
|
1797
1796
|
* @param context The rule context
|
|
1798
1797
|
* @param node The AST node to check
|
|
1799
1798
|
* @returns `true` if the node is inside `createElement`'s props
|
|
@@ -1835,7 +1834,7 @@ function create$27(context) {
|
|
|
1835
1834
|
const classComponents = collectorLegacy.ctx.getAllComponents(program);
|
|
1836
1835
|
for (const lazy of lazyComponentDeclarations) if (AST.findParentNode(lazy, (n) => {
|
|
1837
1836
|
if (AST.isJSX(n)) return true;
|
|
1838
|
-
if (n.type === AST_NODE_TYPES.CallExpression) return
|
|
1837
|
+
if (n.type === AST_NODE_TYPES.CallExpression) return isHookCall(n) || isCreateElementCall(context, n) || isCreateContextCall(context, n);
|
|
1839
1838
|
if (AST.isFunction(n)) return functionComponents.some((c) => c.node === n);
|
|
1840
1839
|
if (AST.isClass(n)) return classComponents.some((c) => c.node === n);
|
|
1841
1840
|
return false;
|
|
@@ -2074,7 +2073,7 @@ const RULE_NAME$20 = "no-unnecessary-key";
|
|
|
2074
2073
|
var no_unnecessary_key_default = createRule({
|
|
2075
2074
|
meta: {
|
|
2076
2075
|
type: "problem",
|
|
2077
|
-
docs: { description: "Disallows unnecessary 'key' props on elements." },
|
|
2076
|
+
docs: { description: "Disallows unnecessary 'key' props on nested child elements when rendering lists." },
|
|
2078
2077
|
messages: { noUnnecessaryKey: "Unnecessary `key` prop on this element. {{reason}}" },
|
|
2079
2078
|
schema: []
|
|
2080
2079
|
},
|
|
@@ -2088,44 +2087,26 @@ function create$20(context) {
|
|
|
2088
2087
|
...getJsxConfigFromContext(context),
|
|
2089
2088
|
...getJsxConfigFromAnnotation(context)
|
|
2090
2089
|
};
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
context.report({
|
|
2112
|
-
messageId: "noUnnecessaryKey",
|
|
2113
|
-
node,
|
|
2114
|
-
data: { reason: "A parent element already has a `key` prop in the same list rendering context." }
|
|
2115
|
-
});
|
|
2116
|
-
},
|
|
2117
|
-
"Program:exit"(node) {
|
|
2118
|
-
const components = ctx.getAllComponents(node);
|
|
2119
|
-
for (const key of constantKeys) {
|
|
2120
|
-
if (AST.findParentNode(key, (n) => AST.isConditional(n) || AST.isControlFlow(n) || findEnclosingAssignmentTarget(n) != null || components.some((comp) => comp.node === n && comp.rets.length > 1)) != null) continue;
|
|
2121
|
-
context.report({
|
|
2122
|
-
messageId: "noUnnecessaryKey",
|
|
2123
|
-
node: key,
|
|
2124
|
-
data: { reason: "The `key` prop is not needed outside of dynamic rendering contexts." }
|
|
2125
|
-
});
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
});
|
|
2090
|
+
return { JSXAttribute(node) {
|
|
2091
|
+
if (node.name.name !== "key") return;
|
|
2092
|
+
const jsxElement = node.parent.parent;
|
|
2093
|
+
if (isJsxFragmentElement(context, jsxElement, jsxConfig)) return;
|
|
2094
|
+
if (jsxElement.openingElement.attributes.some((attr) => attr.type === AST_NODE_TYPES.JSXSpreadAttribute)) return;
|
|
2095
|
+
if (AST.findParentNode(jsxElement, (n) => isRenderFunctionLoose(context, n)) != null) return;
|
|
2096
|
+
const mapCallback = AST.findParentNode(jsxElement, isArrayMethodCallback);
|
|
2097
|
+
if (mapCallback == null || AST.findParentNode(jsxElement, AST.isFunction) !== mapCallback) return;
|
|
2098
|
+
if (context.sourceCode.getScope(mapCallback) !== context.sourceCode.getScope(jsxElement)) return;
|
|
2099
|
+
const keyedElementOrElse = AST.findParentNode(jsxElement, (n) => {
|
|
2100
|
+
if (n === mapCallback) return true;
|
|
2101
|
+
return AST.isJSXElement(n) && getJsxAttribute(context, n)("key") != null;
|
|
2102
|
+
});
|
|
2103
|
+
if (keyedElementOrElse == null || keyedElementOrElse === mapCallback) return;
|
|
2104
|
+
context.report({
|
|
2105
|
+
messageId: "noUnnecessaryKey",
|
|
2106
|
+
node,
|
|
2107
|
+
data: { reason: "A parent element already has a `key` prop in the same list rendering context." }
|
|
2108
|
+
});
|
|
2109
|
+
} };
|
|
2129
2110
|
}
|
|
2130
2111
|
function getArrayMethodCallbackPosition(methodName) {
|
|
2131
2112
|
switch (methodName) {
|
|
@@ -2496,7 +2477,7 @@ function create$12(context) {
|
|
|
2496
2477
|
const valueExpression = value.expression;
|
|
2497
2478
|
const construction = getObjectType(valueExpression, context.sourceCode.getScope(valueExpression));
|
|
2498
2479
|
if (construction == null) return;
|
|
2499
|
-
if (
|
|
2480
|
+
if (isHookCall(construction.node)) return;
|
|
2500
2481
|
getOrElseUpdate(constructions, functionEntry.node, () => []).push(construction);
|
|
2501
2482
|
},
|
|
2502
2483
|
"Program:exit"(program) {
|
|
@@ -2577,7 +2558,7 @@ function create$11(context, [options]) {
|
|
|
2577
2558
|
const { right } = value;
|
|
2578
2559
|
const construction = getObjectType(value, context.sourceCode.getScope(value));
|
|
2579
2560
|
if (construction == null) continue;
|
|
2580
|
-
if (
|
|
2561
|
+
if (isHookCall(construction.node)) continue;
|
|
2581
2562
|
if (safePatterns.length > 0) {
|
|
2582
2563
|
const identifier = extractIdentifier(right);
|
|
2583
2564
|
if (identifier != null && safePatterns.some((pattern) => pattern.test(identifier))) continue;
|
|
@@ -2969,7 +2950,7 @@ function create$7(context) {
|
|
|
2969
2950
|
const hookCalls = /* @__PURE__ */ new Set();
|
|
2970
2951
|
return {
|
|
2971
2952
|
CallExpression(node) {
|
|
2972
|
-
if (!
|
|
2953
|
+
if (!isHookCall(node)) return;
|
|
2973
2954
|
hookCalls.add(node);
|
|
2974
2955
|
},
|
|
2975
2956
|
ImportDeclaration(node) {
|
|
@@ -3353,7 +3334,7 @@ function create$1(context) {
|
|
|
3353
3334
|
}
|
|
3354
3335
|
for (const expr of AST.getNestedCallExpressions(useStateInput)) {
|
|
3355
3336
|
if (!("name" in expr.callee)) continue;
|
|
3356
|
-
if (
|
|
3337
|
+
if (isHookName(expr.callee.name)) continue;
|
|
3357
3338
|
if (ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
3358
3339
|
if (AST.findParentNode(expr, isUseCall) != null) continue;
|
|
3359
3340
|
context.report({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5-beta.11",
|
|
4
4
|
"description": "A set of composable ESLint rules for for libraries and frameworks that use React as a UI runtime.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -37,23 +37,23 @@
|
|
|
37
37
|
"./package.json"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@typescript-eslint/scope-manager": "^8.
|
|
41
|
-
"@typescript-eslint/type-utils": "^8.
|
|
42
|
-
"@typescript-eslint/types": "^8.
|
|
43
|
-
"@typescript-eslint/utils": "^8.
|
|
40
|
+
"@typescript-eslint/scope-manager": "^8.54.0",
|
|
41
|
+
"@typescript-eslint/type-utils": "^8.54.0",
|
|
42
|
+
"@typescript-eslint/types": "^8.54.0",
|
|
43
|
+
"@typescript-eslint/utils": "^8.54.0",
|
|
44
44
|
"compare-versions": "^6.1.1",
|
|
45
45
|
"is-immutable-type": "^5.0.1",
|
|
46
46
|
"string-ts": "^2.3.1",
|
|
47
47
|
"ts-api-utils": "^2.4.0",
|
|
48
48
|
"ts-pattern": "^5.9.0",
|
|
49
|
-
"@eslint-react/ast": "2.7.
|
|
50
|
-
"@eslint-react/core": "2.7.
|
|
51
|
-
"@eslint-react/eff": "2.7.
|
|
52
|
-
"@eslint-react/shared": "2.7.
|
|
53
|
-
"@eslint-react/var": "2.7.
|
|
49
|
+
"@eslint-react/ast": "2.7.5-beta.11",
|
|
50
|
+
"@eslint-react/core": "2.7.5-beta.11",
|
|
51
|
+
"@eslint-react/eff": "2.7.5-beta.11",
|
|
52
|
+
"@eslint-react/shared": "2.7.5-beta.11",
|
|
53
|
+
"@eslint-react/var": "2.7.5-beta.11"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@types/react": "^19.2.
|
|
56
|
+
"@types/react": "^19.2.10",
|
|
57
57
|
"@types/react-dom": "^19.2.3",
|
|
58
58
|
"tsdown": "^0.20.1",
|
|
59
59
|
"@local/configs": "0.0.0"
|