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.
Files changed (2) hide show
  1. package/dist/index.js +70 -48
  2. 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.4";
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.SkipArrayMapArgument | ComponentDetectionHint.SkipNullLiteral | ComponentDetectionHint.SkipUndefined | ComponentDetectionHint.SkipBooleanLiteral | ComponentDetectionHint.SkipStringLiteral | ComponentDetectionHint.SkipNumberLiteral | ComponentDetectionHint.StrictLogical | ComponentDetectionHint.StrictConditional });
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
- return { JSXAttribute(node) {
2129
- if (node.name.name !== "key") return;
2130
- const jsxElement = node.parent.parent;
2131
- if (isJsxFragmentElement(context, jsxElement, jsxConfig)) return;
2132
- if (jsxElement.openingElement.attributes.some((attr) => attr.type === AST_NODE_TYPES.JSXSpreadAttribute)) return;
2133
- if (AST.findParentNode(jsxElement, (n) => isRenderFunctionLoose(context, n)) != null) return;
2134
- const mapCallback = AST.findParentNode(jsxElement, isArrayMethodCallback);
2135
- if (mapCallback == null || AST.findParentNode(jsxElement, AST.isFunction) !== mapCallback) {
2136
- if (!(AST.findParentNode(jsxElement, (n) => AST.isConditional(n) || AST.isControlFlow(n) || findEnclosingAssignmentTarget(n) != null) != null)) context.report({
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: "The `key` prop is not needed outside of dynamic rendering contexts." }
2152
+ data: { reason: "A parent element already has a `key` prop in the same list rendering context." }
2140
2153
  });
2141
- return;
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
- 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({
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 { CallExpression(node) {
2183
- if (!isUseCallbackCall(node)) return;
2184
- const checkForUsageInsideUseEffectReport = checkForUsageInsideUseEffect$1(context.sourceCode, node);
2185
- const initialScope = context.sourceCode.getScope(node);
2186
- const component = context.sourceCode.getScope(node).block;
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] = node.arguments;
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, initialScope), 0);
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, initialScope), 0);
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) => !(ref.init ?? false));
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 { CallExpression(node) {
2256
- const initialScope = context.sourceCode.getScope(node);
2257
- if (!isUseMemoCall(node)) return;
2258
- const checkForUsageInsideUseEffectReport = checkForUsageInsideUseEffect(context.sourceCode, node);
2259
- const component = context.sourceCode.getScope(node).block;
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] = node.arguments;
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, initialScope), 0);
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, initialScope), 0);
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) => !(ref.init ?? false));
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 componentBlocks = new Set(ctx.getAllComponents(program).map((component) => component.node));
3265
+ const components = ctx.getAllComponents(program);
3246
3266
  function isFunctionComponent(block) {
3247
- if (!AST.isFunction(block)) return false;
3248
- const id = AST.getFunctionId(block);
3249
- return id != null && isComponentNameLoose(id.name) && componentBlocks.has(block);
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.4",
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.4",
50
- "@eslint-react/eff": "2.6.4",
51
- "@eslint-react/core": "2.6.4",
52
- "@eslint-react/shared": "2.6.4",
53
- "@eslint-react/var": "2.6.4"
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",