eslint-plugin-react-x 2.6.4 → 2.6.5-beta.0
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 +70 -48
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -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.6.
|
|
71
|
+
var version = "2.6.5-beta.0";
|
|
72
72
|
|
|
73
73
|
//#endregion
|
|
74
74
|
//#region src/utils/create-rule.ts
|
|
@@ -1733,7 +1733,7 @@ var no_nested_component_definitions_default = createRule({
|
|
|
1733
1733
|
defaultOptions: []
|
|
1734
1734
|
});
|
|
1735
1735
|
function create$28(context) {
|
|
1736
|
-
const fCollector = useComponentCollector(context, { hint: ComponentDetectionHint.
|
|
1736
|
+
const fCollector = useComponentCollector(context, { hint: ComponentDetectionHint.SkipArrayMapCallback | ComponentDetectionHint.SkipNullLiteral | ComponentDetectionHint.SkipUndefined | ComponentDetectionHint.SkipBooleanLiteral | ComponentDetectionHint.SkipStringLiteral | ComponentDetectionHint.SkipNumberLiteral | ComponentDetectionHint.StrictLogical | ComponentDetectionHint.StrictConditional });
|
|
1737
1737
|
const cCollector = useComponentCollectorLegacy();
|
|
1738
1738
|
return {
|
|
1739
1739
|
...fCollector.listeners,
|
|
@@ -2125,33 +2125,45 @@ function create$20(context) {
|
|
|
2125
2125
|
...getJsxConfigFromContext(context),
|
|
2126
2126
|
...getJsxConfigFromAnnotation(context)
|
|
2127
2127
|
};
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
if (
|
|
2128
|
+
const { ctx, listeners } = useComponentCollector(context);
|
|
2129
|
+
const constantKeys = /* @__PURE__ */ new Set();
|
|
2130
|
+
return {
|
|
2131
|
+
...listeners,
|
|
2132
|
+
JSXAttribute(node) {
|
|
2133
|
+
if (node.name.name !== "key") return;
|
|
2134
|
+
const jsxElement = node.parent.parent;
|
|
2135
|
+
if (isJsxFragmentElement(context, jsxElement, jsxConfig)) return;
|
|
2136
|
+
if (jsxElement.openingElement.attributes.some((attr) => attr.type === AST_NODE_TYPES.JSXSpreadAttribute)) return;
|
|
2137
|
+
if (AST.findParentNode(jsxElement, (n) => isRenderFunctionLoose(context, n)) != null) return;
|
|
2138
|
+
const mapCallback = AST.findParentNode(jsxElement, isArrayMethodCallback);
|
|
2139
|
+
if (mapCallback == null || AST.findParentNode(jsxElement, AST.isFunction) !== mapCallback) {
|
|
2140
|
+
constantKeys.add(node);
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
if (context.sourceCode.getScope(mapCallback) !== context.sourceCode.getScope(jsxElement)) return;
|
|
2144
|
+
const keyedElementOrElse = AST.findParentNode(jsxElement, (n) => {
|
|
2145
|
+
if (n === mapCallback) return true;
|
|
2146
|
+
return AST.isJSXElement(n) && getJsxAttribute(context, n)("key") != null;
|
|
2147
|
+
});
|
|
2148
|
+
if (keyedElementOrElse == null || keyedElementOrElse === mapCallback) return;
|
|
2149
|
+
context.report({
|
|
2137
2150
|
messageId: "noUnnecessaryKey",
|
|
2138
2151
|
node,
|
|
2139
|
-
data: { reason: "
|
|
2152
|
+
data: { reason: "A parent element already has a `key` prop in the same list rendering context." }
|
|
2140
2153
|
});
|
|
2141
|
-
|
|
2154
|
+
},
|
|
2155
|
+
"Program:exit"(node) {
|
|
2156
|
+
const components = ctx.getAllComponents(node);
|
|
2157
|
+
for (const key of constantKeys) {
|
|
2158
|
+
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) return;
|
|
2159
|
+
context.report({
|
|
2160
|
+
messageId: "noUnnecessaryKey",
|
|
2161
|
+
node,
|
|
2162
|
+
data: { reason: "The `key` prop is not needed outside of dynamic rendering contexts." }
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2142
2165
|
}
|
|
2143
|
-
|
|
2144
|
-
const keyedElementOrElse = AST.findParentNode(jsxElement, (n) => {
|
|
2145
|
-
if (n === mapCallback) return true;
|
|
2146
|
-
return AST.isJSXElement(n) && getJsxAttribute(context, n)("key") != null;
|
|
2147
|
-
});
|
|
2148
|
-
if (keyedElementOrElse == null || keyedElementOrElse === mapCallback) return;
|
|
2149
|
-
context.report({
|
|
2150
|
-
messageId: "noUnnecessaryKey",
|
|
2151
|
-
node,
|
|
2152
|
-
data: { reason: "A parent element already has a `key` prop in the same list rendering context." }
|
|
2153
|
-
});
|
|
2154
|
-
} };
|
|
2166
|
+
};
|
|
2155
2167
|
}
|
|
2156
2168
|
function isArrayMethodCallback(node) {
|
|
2157
2169
|
const parent = node.parent;
|
|
@@ -2179,16 +2191,19 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
2179
2191
|
});
|
|
2180
2192
|
function create$19(context) {
|
|
2181
2193
|
if (!context.sourceCode.text.includes("useCallback")) return {};
|
|
2182
|
-
return {
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
const
|
|
2186
|
-
|
|
2194
|
+
return { VariableDeclarator(node) {
|
|
2195
|
+
const { id, init } = node;
|
|
2196
|
+
if (id.type !== AST_NODE_TYPES.Identifier || init?.type !== AST_NODE_TYPES.CallExpression || !isUseCallbackCall(init)) return;
|
|
2197
|
+
const [cbk, ...rest] = context.sourceCode.getDeclaredVariables(node);
|
|
2198
|
+
if (cbk == null || rest.length > 0) return;
|
|
2199
|
+
const checkForUsageInsideUseEffectReport = checkForUsageInsideUseEffect$1(context.sourceCode, init);
|
|
2200
|
+
const scope = context.sourceCode.getScope(init);
|
|
2201
|
+
const component = context.sourceCode.getScope(init).block;
|
|
2187
2202
|
if (!AST.isFunction(component)) return;
|
|
2188
|
-
const [arg0, arg1] =
|
|
2203
|
+
const [arg0, arg1] = init.arguments;
|
|
2189
2204
|
if (arg0 == null || arg1 == null) return;
|
|
2190
2205
|
if (!match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
2191
|
-
const variableNode = getVariableDefinitionNode(findVariable(n.name,
|
|
2206
|
+
const variableNode = getVariableDefinitionNode(findVariable(n.name, scope), 0);
|
|
2192
2207
|
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) return false;
|
|
2193
2208
|
return variableNode.elements.length === 0;
|
|
2194
2209
|
}).otherwise(() => false)) {
|
|
@@ -2199,7 +2214,7 @@ function create$19(context) {
|
|
|
2199
2214
|
if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) return n.body;
|
|
2200
2215
|
return n;
|
|
2201
2216
|
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
2202
|
-
const variableNode = getVariableDefinitionNode(findVariable(n.name,
|
|
2217
|
+
const variableNode = getVariableDefinitionNode(findVariable(n.name, scope), 0);
|
|
2203
2218
|
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) return null;
|
|
2204
2219
|
return variableNode;
|
|
2205
2220
|
}).otherwise(() => null);
|
|
@@ -2218,7 +2233,8 @@ function checkForUsageInsideUseEffect$1(sourceCode, node) {
|
|
|
2218
2233
|
if (!/use\w*Effect/u.test(sourceCode.text)) return;
|
|
2219
2234
|
if (!isVariableDeclarator(node.parent)) return;
|
|
2220
2235
|
if (!isIdentifier(node.parent.id)) return;
|
|
2221
|
-
const usages = (sourceCode.getDeclaredVariables(node.parent)[0]?.references ?? []).filter((ref) =>
|
|
2236
|
+
const usages = (sourceCode.getDeclaredVariables(node.parent)[0]?.references ?? []).filter((ref) => ref.init !== true);
|
|
2237
|
+
if (usages.length === 0) return;
|
|
2222
2238
|
const effectSet = /* @__PURE__ */ new Set();
|
|
2223
2239
|
for (const usage of usages) {
|
|
2224
2240
|
const effect = AST.findParentNode(usage.identifier, isUseEffectLikeCall);
|
|
@@ -2252,20 +2268,23 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
2252
2268
|
});
|
|
2253
2269
|
function create$18(context) {
|
|
2254
2270
|
if (!context.sourceCode.text.includes("useMemo")) return {};
|
|
2255
|
-
return {
|
|
2256
|
-
const
|
|
2257
|
-
if (!isUseMemoCall(
|
|
2258
|
-
const
|
|
2259
|
-
|
|
2271
|
+
return { VariableDeclarator(node) {
|
|
2272
|
+
const { id, init } = node;
|
|
2273
|
+
if (id.type !== AST_NODE_TYPES.Identifier || init?.type !== AST_NODE_TYPES.CallExpression || !isUseMemoCall(init)) return;
|
|
2274
|
+
const [mem, ...rest] = context.sourceCode.getDeclaredVariables(node);
|
|
2275
|
+
if (mem == null || rest.length > 0) return;
|
|
2276
|
+
const checkForUsageInsideUseEffectReport = checkForUsageInsideUseEffect(context.sourceCode, init);
|
|
2277
|
+
const scope = context.sourceCode.getScope(init);
|
|
2278
|
+
const component = scope.block;
|
|
2260
2279
|
if (!AST.isFunction(component)) return;
|
|
2261
|
-
const [arg0, arg1] =
|
|
2280
|
+
const [arg0, arg1] = init.arguments;
|
|
2262
2281
|
if (arg0 == null || arg1 == null) return;
|
|
2263
2282
|
if (AST.isFunction(arg0) && [...AST.getNestedCallExpressions(arg0.body), ...AST.getNestedNewExpressions(arg0.body)].length > 0) {
|
|
2264
2283
|
report(context)(checkForUsageInsideUseEffectReport);
|
|
2265
2284
|
return;
|
|
2266
2285
|
}
|
|
2267
2286
|
if (!match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
2268
|
-
const variableNode = getVariableDefinitionNode(findVariable(n.name,
|
|
2287
|
+
const variableNode = getVariableDefinitionNode(findVariable(n.name, scope), 0);
|
|
2269
2288
|
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) return false;
|
|
2270
2289
|
return variableNode.elements.length === 0;
|
|
2271
2290
|
}).otherwise(() => false)) {
|
|
@@ -2276,7 +2295,7 @@ function create$18(context) {
|
|
|
2276
2295
|
if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) return n.body;
|
|
2277
2296
|
return n;
|
|
2278
2297
|
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
2279
|
-
const variableNode = getVariableDefinitionNode(findVariable(n.name,
|
|
2298
|
+
const variableNode = getVariableDefinitionNode(findVariable(n.name, scope), 0);
|
|
2280
2299
|
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) return null;
|
|
2281
2300
|
return variableNode;
|
|
2282
2301
|
}).otherwise(() => null);
|
|
@@ -2295,7 +2314,8 @@ function checkForUsageInsideUseEffect(sourceCode, node) {
|
|
|
2295
2314
|
if (!/use\w*Effect/u.test(sourceCode.text)) return;
|
|
2296
2315
|
if (!isVariableDeclarator(node.parent)) return;
|
|
2297
2316
|
if (!isIdentifier(node.parent.id)) return;
|
|
2298
|
-
const usages = (sourceCode.getDeclaredVariables(node.parent)[0]?.references ?? []).filter((ref) =>
|
|
2317
|
+
const usages = (sourceCode.getDeclaredVariables(node.parent)[0]?.references ?? []).filter((ref) => ref.init !== true);
|
|
2318
|
+
if (usages.length === 0) return;
|
|
2299
2319
|
const effectSet = /* @__PURE__ */ new Set();
|
|
2300
2320
|
for (const usage of usages) {
|
|
2301
2321
|
const effect = AST.findParentNode(usage.identifier, isUseEffectLikeCall);
|
|
@@ -2329,7 +2349,7 @@ var no_unnecessary_use_prefix_default = createRule({
|
|
|
2329
2349
|
defaultOptions: []
|
|
2330
2350
|
});
|
|
2331
2351
|
function create$17(context) {
|
|
2332
|
-
const { ctx, listeners } = useHookCollector();
|
|
2352
|
+
const { ctx, listeners } = useHookCollector(context);
|
|
2333
2353
|
return {
|
|
2334
2354
|
...listeners,
|
|
2335
2355
|
"Program:exit"(program) {
|
|
@@ -3242,11 +3262,13 @@ function create$4(context) {
|
|
|
3242
3262
|
}
|
|
3243
3263
|
},
|
|
3244
3264
|
"Program:exit"(program) {
|
|
3245
|
-
const
|
|
3265
|
+
const components = ctx.getAllComponents(program);
|
|
3246
3266
|
function isFunctionComponent(block) {
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3267
|
+
return components.some((comp) => {
|
|
3268
|
+
if (comp.node !== block) return false;
|
|
3269
|
+
if (comp.name == null && AST.findParentNode(comp.node, (n) => n.type === AST_NODE_TYPES.ExportDefaultDeclaration) != null) return false;
|
|
3270
|
+
return true;
|
|
3271
|
+
});
|
|
3250
3272
|
}
|
|
3251
3273
|
for (const [initialScope, expr] of exprs) {
|
|
3252
3274
|
let scope = initialScope;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.5-beta.0",
|
|
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",
|
|
@@ -46,11 +46,11 @@
|
|
|
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.6.
|
|
50
|
-
"@eslint-react/eff": "2.6.
|
|
51
|
-
"@eslint-react/core": "2.6.
|
|
52
|
-
"@eslint-react/
|
|
53
|
-
"@eslint-react/
|
|
49
|
+
"@eslint-react/ast": "2.6.5-beta.0",
|
|
50
|
+
"@eslint-react/eff": "2.6.5-beta.0",
|
|
51
|
+
"@eslint-react/core": "2.6.5-beta.0",
|
|
52
|
+
"@eslint-react/var": "2.6.5-beta.0",
|
|
53
|
+
"@eslint-react/shared": "2.6.5-beta.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/react": "^19.2.8",
|