eslint-plugin-react-x 5.14.5 → 5.14.6

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 +30 -77
  2. package/package.json +10 -10
package/dist/index.js CHANGED
@@ -144,7 +144,7 @@ const rules$6 = {
144
144
  //#endregion
145
145
  //#region package.json
146
146
  var name$6 = "eslint-plugin-react-x";
147
- var version = "5.14.5";
147
+ var version = "5.14.6";
148
148
 
149
149
  //#endregion
150
150
  //#region src/utils/create-rule.ts
@@ -1344,103 +1344,55 @@ const MUTATING_METHODS = /* @__PURE__ */ new Set([
1344
1344
  "splice",
1345
1345
  "unshift"
1346
1346
  ]);
1347
- /**
1348
- * Check if `inner` is fully contained within `outer`'s source range.
1349
- * @param inner The node that may be contained.
1350
- * @param outer The node that may contain it.
1351
- */
1352
- function isNodeWithin(inner, outer) {
1353
- return inner.range[0] >= outer.range[0] && inner.range[1] <= outer.range[1];
1354
- }
1355
- /**
1356
- * Resolve an expression to the function it ultimately refers to, following
1357
- * simple local aliasing (`const fn2 = fn;`) via scope resolution.
1358
- * @param context The ESLint rule context.
1359
- * @param node The expression to resolve.
1360
- * @param seen Identifiers already visited, to guard against cycles.
1361
- */
1347
+ const isUseRouterCall = core.isAPICall("useRouter");
1362
1348
  function resolveToFunctionNode(context, node, seen = /* @__PURE__ */ new Set()) {
1363
1349
  const expr = Extract.unwrap(node);
1364
1350
  if (Check.isFunction(expr)) return expr;
1365
- if (expr.type !== AST_NODE_TYPES.Identifier) return null;
1366
- if (seen.has(expr)) return null;
1351
+ if (expr.type !== AST_NODE_TYPES.Identifier || seen.has(expr)) return null;
1367
1352
  seen.add(expr);
1368
1353
  const resolved = resolve(context, expr);
1369
- if (resolved == null) return null;
1370
- return resolveToFunctionNode(context, resolved, seen);
1354
+ return resolved == null ? null : resolveToFunctionNode(context, resolved, seen);
1371
1355
  }
1372
- /**
1373
- * Follow identifier-only variable-declarator aliases to their originating
1374
- * binding. Assignment aliases are intentionally left for the value-flow phase.
1375
- * @param context The ESLint rule context.
1376
- * @param variable The binding whose initializer aliases should be followed.
1377
- * @param seen Bindings already visited, to guard against cycles.
1378
- */
1379
1356
  function resolveVariableOrigin(context, variable, seen = /* @__PURE__ */ new Set()) {
1380
1357
  if (seen.has(variable)) return variable;
1381
1358
  seen.add(variable);
1382
- const definition = variable.defs.length === 1 ? variable.defs[0] : null;
1383
- if (definition?.type !== DefinitionType.Variable || definition.node.init == null) return variable;
1384
- const initializer = Extract.unwrap(definition.node.init);
1385
- if (initializer.type !== AST_NODE_TYPES.Identifier) return variable;
1386
- const source = findVariable(context.sourceCode.getScope(initializer), initializer);
1387
- if (source == null) return variable;
1388
- return resolveVariableOrigin(context, source, seen);
1359
+ const def = variable.defs.length === 1 ? variable.defs[0] : null;
1360
+ if (def?.type !== DefinitionType.Variable || def.node.init == null) return variable;
1361
+ const init = Extract.unwrap(def.node.init);
1362
+ if (init.type !== AST_NODE_TYPES.Identifier) return variable;
1363
+ const source = findVariable(context.sourceCode.getScope(init), init);
1364
+ return source == null ? variable : resolveVariableOrigin(context, source, seen);
1389
1365
  }
1390
- /**
1391
- * Check if a name is ref-like ("ref" or ends with "Ref").
1392
- * Refs are mutable by design and exempted from immutability checks.
1393
- * @param name The identifier name to check.
1394
- */
1395
1366
  function isRefLikeName$1(name) {
1396
1367
  return name === "ref" || name.endsWith("Ref");
1397
1368
  }
1398
- /**
1399
- * Check if any identifier or property name in a member-expression chain
1400
- * is ref-like.
1401
- * @param node The AST node to inspect.
1402
- */
1403
1369
  function hasRefLikeNameInChain(node) {
1404
1370
  if (node.type === AST_NODE_TYPES.Identifier) return isRefLikeName$1(node.name);
1405
- if (node.type === AST_NODE_TYPES.MemberExpression) {
1406
- const propName = Extract.getPropertyName(node.property);
1407
- if (propName != null && isRefLikeName$1(propName)) return true;
1408
- return hasRefLikeNameInChain(node.object);
1409
- }
1410
- return false;
1371
+ if (node.type !== AST_NODE_TYPES.MemberExpression) return false;
1372
+ const propertyName = Extract.getPropertyName(node.property);
1373
+ return propertyName != null ? isRefLikeName$1(propertyName) || hasRefLikeNameInChain(node.object) : hasRefLikeNameInChain(node.object);
1411
1374
  }
1412
- /**
1413
- * Check if the root identifier of a member-expression chain is initialized
1414
- * from a `useRef()` call, following variable-declarator aliases.
1415
- * @param context The ESLint rule context.
1416
- * @param node The AST node to inspect (an identifier or member-expression chain).
1417
- */
1418
- function isInitializedFromUseRef(context, node) {
1375
+ function isInitializedFromCall(context, node, isCall) {
1419
1376
  const root = node.type === AST_NODE_TYPES.Identifier ? node : Extract.getRootIdentifier(node);
1420
1377
  if (root == null) return false;
1421
1378
  const variable = findVariable(context.sourceCode.getScope(root), root);
1422
1379
  if (variable == null) return false;
1423
- return isVariableInitializedFromUseRef(context, variable, /* @__PURE__ */ new Set());
1380
+ const origin = resolveVariableOrigin(context, variable);
1381
+ const def = origin.defs.length === 1 ? origin.defs[0] : null;
1382
+ if (def?.type !== DefinitionType.Variable || def.node.init == null) return false;
1383
+ const init = Extract.unwrap(def.node.init);
1384
+ return init.type === AST_NODE_TYPES.CallExpression && isCall(init);
1424
1385
  }
1425
- function isVariableInitializedFromUseRef(context, variable, seen) {
1426
- if (seen.has(variable)) return false;
1427
- seen.add(variable);
1428
- const definition = variable.defs.length === 1 ? variable.defs[0] : null;
1429
- if (definition?.type !== DefinitionType.Variable || definition.node.init == null) return false;
1430
- const initializer = Extract.unwrap(definition.node.init);
1431
- if (initializer.type === AST_NODE_TYPES.CallExpression) return core.isUseRefCall(context, initializer);
1432
- if (initializer.type !== AST_NODE_TYPES.Identifier) return false;
1433
- const source = findVariable(context.sourceCode.getScope(initializer), initializer);
1434
- return source != null && isVariableInitializedFromUseRef(context, source, seen);
1386
+ function isInitializedFromUseRef(context, node) {
1387
+ return isInitializedFromCall(context, node, (init) => core.isUseRefCall(context, init));
1388
+ }
1389
+ function isInitializedFromUseRouter(context, node) {
1390
+ return isInitializedFromCall(context, node, (init) => isUseRouterCall(context, init));
1391
+ }
1392
+ function isKnownNonMutatingMethodCall(context, node) {
1393
+ const callee = Extract.unwrap(node.callee);
1394
+ return Check.isExpression(callee) && isInitializedFromUseRouter(context, callee);
1435
1395
  }
1436
- /**
1437
- * Check if a mutated expression chain should be exempt from immutability
1438
- * checks because it is rooted at a ref: either by naming convention
1439
- * ({@link hasRefLikeNameInChain}) or because it is initialized from a
1440
- * `useRef()` call ({@link isInitializedFromUseRef}).
1441
- * @param context The ESLint rule context.
1442
- * @param node The AST node to inspect (an identifier or member-expression chain).
1443
- */
1444
1396
  function isRefLikeChain(context, node) {
1445
1397
  return hasRefLikeNameInChain(node) || isInitializedFromUseRef(context, node);
1446
1398
  }
@@ -1542,6 +1494,7 @@ function isRefMutation(context, mutation) {
1542
1494
  function inferMutableFunctions(context, mutations) {
1543
1495
  const mutableFunctions = /* @__PURE__ */ new Map();
1544
1496
  for (const mutation of mutations) {
1497
+ if (mutation.node.type === AST_NODE_TYPES.CallExpression && isKnownNonMutatingMethodCall(context, mutation.node)) continue;
1545
1498
  if (isRefMutation(context, mutation)) continue;
1546
1499
  const variable = findVariable(context.sourceCode.getScope(mutation.root), mutation.root);
1547
1500
  if (variable == null) continue;
@@ -1550,7 +1503,7 @@ function inferMutableFunctions(context, mutations) {
1550
1503
  const declaration = origin.identifiers.at(0) ?? null;
1551
1504
  let current = mutation.enclosingFunction;
1552
1505
  while (current != null) {
1553
- if (declaration != null && isNodeWithin(declaration, current)) break;
1506
+ if (declaration != null && Traverse.findParent(declaration, Check.isFunction) === current) break;
1554
1507
  if (!mutableFunctions.has(current)) mutableFunctions.set(current, {
1555
1508
  name: origin.name,
1556
1509
  node: mutation.node
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.14.5",
3
+ "version": "5.14.6",
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,26 +45,26 @@
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/eslint": "5.14.5",
49
- "@eslint-react/ast": "5.14.5",
50
- "@eslint-react/core": "5.14.5",
51
- "@eslint-react/jsx": "5.14.5",
52
- "@eslint-react/shared": "5.14.5",
53
- "@eslint-react/var": "5.14.5"
48
+ "@eslint-react/ast": "5.14.6",
49
+ "@eslint-react/core": "5.14.6",
50
+ "@eslint-react/jsx": "5.14.6",
51
+ "@eslint-react/eslint": "5.14.6",
52
+ "@eslint-react/shared": "5.14.6",
53
+ "@eslint-react/var": "5.14.6"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/react": "^19.2.17",
57
57
  "@types/react-dom": "^19.2.3",
58
58
  "dedent": "^1.7.2",
59
- "eslint": "^10.6.0",
59
+ "eslint": "^10.7.0",
60
60
  "react": "^19.2.7",
61
61
  "react-dom": "^19.2.7",
62
62
  "tsdown": "^0.22.4",
63
63
  "tsl": "^1.0.30",
64
64
  "tsl-dx": "^0.13.2",
65
65
  "typescript": "6.0.3",
66
- "@local/eff": "0.0.0",
67
- "@local/configs": "0.0.0"
66
+ "@local/configs": "0.0.0",
67
+ "@local/eff": "0.0.0"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "eslint": "*",