eslint-plugin-react-x 5.11.3 → 5.12.0-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 +166 -204
  2. package/package.json +9 -9
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.11.3";
147
+ var version = "5.12.0-beta.0";
148
148
 
149
149
  //#endregion
150
150
  //#region src/utils/create-rule.ts
@@ -1146,7 +1146,7 @@ function getUnknownDependenciesMessage(reactiveHookName) {
1146
1146
  * Array methods that mutate the array in place.
1147
1147
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
1148
1148
  */
1149
- const MUTATING_ARRAY_METHODS$1 = /* @__PURE__ */ new Set([
1149
+ const MUTATING_ARRAY_METHODS = /* @__PURE__ */ new Set([
1150
1150
  "copyWithin",
1151
1151
  "fill",
1152
1152
  "pop",
@@ -1253,7 +1253,7 @@ function create$49(context) {
1253
1253
  if (callee.type !== AST_NODE_TYPES.MemberExpression) return;
1254
1254
  const { object, property } = callee;
1255
1255
  if (property.type !== AST_NODE_TYPES.Identifier) return;
1256
- if (!MUTATING_ARRAY_METHODS$1.has(property.name)) return;
1256
+ if (!MUTATING_ARRAY_METHODS.has(property.name)) return;
1257
1257
  const rootId = Extract.getRootIdentifier(object);
1258
1258
  if (rootId == null) return;
1259
1259
  if (!isGlobalOrModuleVariable(rootId)) return;
@@ -1281,21 +1281,52 @@ function create$49(context) {
1281
1281
  //#endregion
1282
1282
  //#region src/rules/immutability/lib.ts
1283
1283
  /**
1284
- * Array methods that mutate the array in place.
1284
+ * Methods that mutate their receiver in place.
1285
1285
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
1286
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
1287
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
1286
1288
  */
1287
- const MUTATING_ARRAY_METHODS = /* @__PURE__ */ new Set([
1289
+ const MUTATING_METHODS = /* @__PURE__ */ new Set([
1290
+ "add",
1291
+ "clear",
1288
1292
  "copyWithin",
1293
+ "delete",
1289
1294
  "fill",
1290
1295
  "pop",
1291
1296
  "push",
1292
1297
  "reverse",
1298
+ "set",
1293
1299
  "shift",
1294
1300
  "sort",
1295
1301
  "splice",
1296
1302
  "unshift"
1297
1303
  ]);
1298
1304
  /**
1305
+ * Check if `inner` is fully contained within `outer`'s source range.
1306
+ * @param inner The node that may be contained.
1307
+ * @param outer The node that may contain it.
1308
+ */
1309
+ function isNodeWithin(inner, outer) {
1310
+ return inner.range[0] >= outer.range[0] && inner.range[1] <= outer.range[1];
1311
+ }
1312
+ /**
1313
+ * Resolve an expression to the function it ultimately refers to, following
1314
+ * simple local aliasing (`const fn2 = fn;`) via scope resolution.
1315
+ * @param context The ESLint rule context.
1316
+ * @param node The expression to resolve.
1317
+ * @param seen Identifiers already visited, to guard against cycles.
1318
+ */
1319
+ function resolveToFunctionNode(context, node, seen = /* @__PURE__ */ new Set()) {
1320
+ const expr = Extract.unwrap(node);
1321
+ if (Check.isFunction(expr)) return expr;
1322
+ if (expr.type !== AST_NODE_TYPES.Identifier) return null;
1323
+ if (seen.has(expr)) return null;
1324
+ seen.add(expr);
1325
+ const resolved = resolve(context, expr);
1326
+ if (resolved == null) return null;
1327
+ return resolveToFunctionNode(context, resolved, seen);
1328
+ }
1329
+ /**
1299
1330
  * Check if a name is ref-like ("ref" or ends with "Ref").
1300
1331
  * Refs are mutable by design and exempted from immutability checks.
1301
1332
  * @param name The identifier name to check.
@@ -1317,25 +1348,6 @@ function hasRefLikeNameInChain(node) {
1317
1348
  }
1318
1349
  return false;
1319
1350
  }
1320
- /**
1321
- * Check if `name` appears anywhere inside a parameter pattern.
1322
- * @param pattern - The parameter pattern to search in.
1323
- * @param name - The identifier name to look for.
1324
- */
1325
- function identifierExistsInPattern(pattern, name) {
1326
- switch (pattern.type) {
1327
- case AST_NODE_TYPES.Identifier: return pattern.name === name;
1328
- case AST_NODE_TYPES.ObjectPattern: return pattern.properties.some((p) => {
1329
- if (p.type === AST_NODE_TYPES.Property) return identifierExistsInPattern(p.value, name);
1330
- return identifierExistsInPattern(p.argument, name);
1331
- });
1332
- case AST_NODE_TYPES.ArrayPattern: return pattern.elements.some((el) => el != null && identifierExistsInPattern(el, name));
1333
- case AST_NODE_TYPES.RestElement: return identifierExistsInPattern(pattern.argument, name);
1334
- case AST_NODE_TYPES.AssignmentPattern: return identifierExistsInPattern(pattern.left, name);
1335
- case AST_NODE_TYPES.MemberExpression: return Extract.getRootIdentifier(pattern)?.name === name;
1336
- default: return false;
1337
- }
1338
- }
1339
1351
 
1340
1352
  //#endregion
1341
1353
  //#region src/rules/immutability/immutability.ts
@@ -1343,11 +1355,10 @@ const RULE_NAME$48 = "immutability";
1343
1355
  var immutability_default = createRule({
1344
1356
  meta: {
1345
1357
  type: "problem",
1346
- docs: { description: "Validates against mutating props, state, and other values that are immutable." },
1358
+ docs: { description: "Validates against passing functions that mutate captured local variables into frozen contexts such as JSX props, hook arguments, and hook return values." },
1347
1359
  messages: {
1348
- mutatingArrayMethod: "Do not call '{{method}}()' on '{{name}}'. Props and state are immutable create a new array instead.",
1349
- mutatingAssignment: "Do not mutate '{{name}}' directly. Props and state are immutable — create a new object instead.",
1350
- noRefLikeStateName: "State variable '{{name}}' should not be named 'ref' or end with 'Ref'. Use a different name to avoid confusion with mutable refs."
1360
+ default: "This function may (indirectly) reassign or modify '{{name}}' after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.",
1361
+ mutates: "This modifies '{{name}}'."
1351
1362
  },
1352
1363
  schema: []
1353
1364
  },
@@ -1356,166 +1367,117 @@ var immutability_default = createRule({
1356
1367
  defaultOptions: []
1357
1368
  });
1358
1369
  function create$48(context) {
1359
- const { additionalStateHooks } = getSettingsFromContext(context);
1360
1370
  const hc = core.getHookCollector(context);
1361
- const fc = core.getFunctionComponentCollector(context);
1362
- /**
1363
- * Violations accumulated while traversing. Each entry records the node to
1364
- * report and the enclosing function so we can filter at Program:exit.
1365
- */
1366
- const violations = [];
1367
- function isUseStateCall(node) {
1368
- return core.isUseStateLikeCall(node, additionalStateHooks);
1369
- }
1370
- /**
1371
- * Return true when `id` is the *value* variable (index 0) produced by a
1372
- * `useState(…)` or `useReducer(…)` call.
1373
- * @param id The identifier to check. May be a reference to the state variable, e.g. used inside an event handler nested in the component body — scope resolution will trace it back to the original declaration.
1374
- * @returns True if `id` is a state variable, false otherwise.
1375
- */
1376
- function isStateValue(id) {
1377
- const initNode = resolve(context, id);
1378
- if (initNode == null || initNode.type !== AST_NODE_TYPES.CallExpression) return false;
1379
- if (!isUseStateCall(initNode) && !core.isUseReducerCall(context, initNode)) return false;
1380
- const declarator = initNode.parent;
1381
- if (!("id" in declarator) || declarator.id?.type !== AST_NODE_TYPES.ArrayPattern) return true;
1382
- return declarator.id.elements.findIndex((el) => el?.type === AST_NODE_TYPES.Identifier && el.name === id.name) === 0;
1371
+ const mutationSites = [];
1372
+ const sinkCandidates = [];
1373
+ function pushMutationSite(node, root) {
1374
+ const enclosing = Traverse.findParent(node, Check.isFunction);
1375
+ if (enclosing == null) return;
1376
+ mutationSites.push({
1377
+ enclosing,
1378
+ node,
1379
+ root
1380
+ });
1383
1381
  }
1384
- /**
1385
- * Return the function node when `id` is a parameter of any position in any
1386
- * ancestor function; otherwise return `null`.
1387
- *
1388
- * The caller must later verify (at Program:exit) that the returned function
1389
- * is actually a component or hook — event handler parameters share the same
1390
- * syntactic shape but should **not** be treated as React props.
1391
- *
1392
- * Uses scope resolution so that references to parameters inside nested arrow
1393
- * functions are correctly traced back to the declaring function, e.g.:
1394
- *
1395
- * function Component({ items }) { // ← items defined here
1396
- * const handleClick = () => {
1397
- * items.push(4); // ← items resolved via scope to Component's param
1398
- * };
1399
- * }
1400
- * @param id The identifier to check.
1401
- * @returns The function node where `id` is a parameter, or `null`.
1402
- */
1403
- function getPropsDefiningFunction(id) {
1404
- const variable = findVariable(context.sourceCode.getScope(id), id);
1405
- if (variable == null) return null;
1406
- for (const def of variable.defs) {
1407
- if (def.type !== DefinitionType.Parameter) continue;
1408
- let fn = def.node;
1409
- for (;;) {
1410
- if (Check.isFunction(fn)) break;
1411
- fn = fn.parent ?? null;
1412
- if (fn == null) break;
1382
+ return merge(hc.visitor, {
1383
+ AssignmentExpression(node) {
1384
+ const left = Extract.unwrap(node.left);
1385
+ switch (left.type) {
1386
+ case AST_NODE_TYPES.Identifier:
1387
+ if (isRefLikeName(left.name)) return;
1388
+ pushMutationSite(node, left);
1389
+ return;
1390
+ case AST_NODE_TYPES.MemberExpression: {
1391
+ if (hasRefLikeNameInChain(left)) return;
1392
+ const rootId = Extract.getRootIdentifier(left);
1393
+ if (rootId == null) return;
1394
+ pushMutationSite(node, rootId);
1395
+ return;
1396
+ }
1413
1397
  }
1414
- if (fn == null) continue;
1415
- const func = fn;
1416
- if (func.params.some((param) => identifierExistsInPattern(param, id.name))) return func;
1417
- }
1418
- return null;
1419
- }
1420
- return merge(hc.visitor, fc.visitor, {
1421
- /**
1422
- * Detect `state.push(…)`, `state.sort()`, etc.
1423
- *
1424
- * Pattern:
1425
- * CallExpression
1426
- * callee: MemberExpression
1427
- * object: <state or props identifier>
1428
- * property: <mutating method name>
1429
- * @param node The CallExpression node to analyze.
1430
- */
1398
+ },
1431
1399
  CallExpression(node) {
1432
- if (isUseStateCall(node) || core.isUseReducerCall(context, node)) {
1433
- const declarator = node.parent;
1434
- if (declarator.type === AST_NODE_TYPES.VariableDeclarator && declarator.id.type === AST_NODE_TYPES.ArrayPattern) {
1435
- const [firstElement] = declarator.id.elements;
1436
- if (firstElement?.type === AST_NODE_TYPES.Identifier && isRefLikeName(firstElement.name)) context.report({
1437
- data: { name: firstElement.name },
1438
- messageId: "noRefLikeStateName",
1439
- node: firstElement
1440
- });
1400
+ const callee = Extract.unwrap(node.callee);
1401
+ if (callee.type === AST_NODE_TYPES.MemberExpression) {
1402
+ const propName = Extract.getPropertyName(callee.property);
1403
+ if (propName != null && MUTATING_METHODS.has(propName) && !hasRefLikeNameInChain(callee.object)) {
1404
+ const rootId = Extract.getRootIdentifier(callee.object);
1405
+ if (rootId != null) pushMutationSite(node, rootId);
1441
1406
  }
1442
1407
  }
1443
- const callee = Extract.unwrap(node.callee);
1444
- if (callee.type !== AST_NODE_TYPES.MemberExpression) return;
1445
- const { object, property } = callee;
1446
- if (property.type !== AST_NODE_TYPES.Identifier) return;
1447
- if (!MUTATING_ARRAY_METHODS.has(property.name)) return;
1448
- const rootId = Extract.getRootIdentifier(object);
1449
- if (rootId == null) return;
1450
- if (rootId.name === "draft") return;
1451
- if (hasRefLikeNameInChain(object)) return;
1452
- const enclosingFn = Traverse.findParent(node, Check.isFunction);
1453
- if (enclosingFn == null) return;
1454
- const isState = isStateValue(rootId);
1455
- const propsDefiningFunc = !isState ? getPropsDefiningFunction(rootId) : null;
1456
- if (!isState && propsDefiningFunc == null) return;
1457
- violations.push({
1458
- data: {
1459
- name: context.sourceCode.getText(object),
1460
- method: property.name
1461
- },
1462
- func: enclosingFn,
1463
- messageId: "mutatingArrayMethod",
1464
- node,
1465
- ...propsDefiningFunc != null ? { propsDefiningFunc } : {}
1466
- });
1408
+ if (core.isHookCall(node)) for (const arg of node.arguments) {
1409
+ if (arg.type === AST_NODE_TYPES.SpreadElement) continue;
1410
+ sinkCandidates.push(arg);
1411
+ }
1467
1412
  },
1468
- /**
1469
- * Detect `state.foo = bar`, `state.foo.bar = baz`, `props.foo = bar`.
1470
- *
1471
- * Pattern:
1472
- * AssignmentExpression
1473
- * left: MemberExpression
1474
- * object: <state or props identifier (or deeper chain)>
1475
- * @param node The AssignmentExpression node to analyze.
1476
- */
1477
- AssignmentExpression(node) {
1478
- if (node.left.type !== AST_NODE_TYPES.MemberExpression) return;
1479
- const rootId = Extract.getRootIdentifier(node.left);
1480
- if (rootId == null) return;
1481
- if (rootId.name === "draft") return;
1482
- if (hasRefLikeNameInChain(node.left.object)) return;
1483
- const enclosingFn = Traverse.findParent(node, Check.isFunction);
1484
- if (enclosingFn == null) return;
1485
- const isState = isStateValue(rootId);
1486
- const propsDefiningFunc = !isState ? getPropsDefiningFunction(rootId) : null;
1487
- if (!isState && propsDefiningFunc == null) return;
1488
- violations.push({
1489
- data: { name: context.sourceCode.getText(node.left.object) },
1490
- func: enclosingFn,
1491
- messageId: "mutatingAssignment",
1492
- node,
1493
- ...propsDefiningFunc != null ? { propsDefiningFunc } : {}
1494
- });
1413
+ JSXAttribute(node) {
1414
+ if (node.value?.type !== AST_NODE_TYPES.JSXExpressionContainer) return;
1415
+ const expr = node.value.expression;
1416
+ if (expr.type === AST_NODE_TYPES.JSXEmptyExpression) return;
1417
+ sinkCandidates.push(expr);
1495
1418
  },
1496
- "Program:exit"(node) {
1497
- const comps = fc.api.getAllComponents(node);
1498
- const hooks = hc.api.getAllHooks(node);
1499
- const funcs = [...comps, ...hooks];
1500
- for (const { data, func, messageId, node, propsDefiningFunc } of violations) {
1501
- let current = func;
1502
- let insideComponentOrHook = false;
1419
+ "Program:exit"(program) {
1420
+ const mutableFunctions = /* @__PURE__ */ new Map();
1421
+ for (const { enclosing, node, root } of mutationSites) {
1422
+ const variable = findVariable(context.sourceCode.getScope(root), root);
1423
+ if (variable == null) continue;
1424
+ const declId = variable.identifiers.at(0) ?? null;
1425
+ let current = enclosing;
1503
1426
  while (current != null) {
1504
- if (funcs.some((f) => f.node === current)) {
1505
- insideComponentOrHook = true;
1506
- break;
1507
- }
1427
+ if (declId != null && isNodeWithin(declId, current)) break;
1428
+ if (!mutableFunctions.has(current)) mutableFunctions.set(current, {
1429
+ name: variable.name,
1430
+ node
1431
+ });
1508
1432
  current = Traverse.findParent(current, Check.isFunction);
1509
1433
  }
1510
- if (!insideComponentOrHook) continue;
1511
- if (propsDefiningFunc != null) {
1512
- if (!funcs.some((f) => f.node === propsDefiningFunc)) continue;
1513
- }
1434
+ }
1435
+ if (mutableFunctions.size === 0) return;
1436
+ const reported = /* @__PURE__ */ new Set();
1437
+ function checkSink(expr) {
1438
+ if (expr == null || reported.has(expr)) return;
1439
+ const fn = resolveToFunctionNode(context, expr);
1440
+ if (fn == null) return;
1441
+ const mutation = mutableFunctions.get(fn);
1442
+ if (mutation == null) return;
1443
+ reported.add(expr);
1514
1444
  context.report({
1515
- data,
1516
- messageId,
1517
- node
1445
+ data: { name: mutation.name },
1446
+ messageId: "default",
1447
+ node: expr
1518
1448
  });
1449
+ context.report({
1450
+ data: { name: mutation.name },
1451
+ messageId: "mutates",
1452
+ node: mutation.node
1453
+ });
1454
+ }
1455
+ for (const candidate of sinkCandidates) checkSink(candidate);
1456
+ for (const hook of hc.api.getAllHooks(program)) for (const ret of hook.rets) checkSink(ret);
1457
+ },
1458
+ UnaryExpression(node) {
1459
+ if (node.operator !== "delete") return;
1460
+ const arg = Extract.unwrap(node.argument);
1461
+ if (arg.type !== AST_NODE_TYPES.MemberExpression) return;
1462
+ if (hasRefLikeNameInChain(arg)) return;
1463
+ const rootId = Extract.getRootIdentifier(arg);
1464
+ if (rootId == null) return;
1465
+ pushMutationSite(node, rootId);
1466
+ },
1467
+ UpdateExpression(node) {
1468
+ const arg = Extract.unwrap(node.argument);
1469
+ switch (arg.type) {
1470
+ case AST_NODE_TYPES.Identifier:
1471
+ if (isRefLikeName(arg.name)) return;
1472
+ pushMutationSite(node, arg);
1473
+ return;
1474
+ case AST_NODE_TYPES.MemberExpression: {
1475
+ if (hasRefLikeNameInChain(arg)) return;
1476
+ const rootId = Extract.getRootIdentifier(arg);
1477
+ if (rootId == null) return;
1478
+ pushMutationSite(node, rootId);
1479
+ return;
1480
+ }
1519
1481
  }
1520
1482
  }
1521
1483
  });
@@ -2678,19 +2640,19 @@ function create$27(context) {
2678
2640
  /**
2679
2641
  * Recursively inspects a node to find potential leaked conditional rendering
2680
2642
  * @param node The AST node to inspect
2681
- * @param visited A set of visited identifier names to prevent infinite recursion.
2643
+ * @param seen A set of visited identifier names to prevent infinite recursion.
2682
2644
  * @returns A report descriptor if a problem is found, otherwise `null`
2683
2645
  */
2684
- function getReportDescriptor(node, visited = /* @__PURE__ */ new Set()) {
2646
+ function getReportDescriptor(node, seen = /* @__PURE__ */ new Set()) {
2685
2647
  if (node == null) return null;
2686
- if (Check.is(AST_NODE_TYPES.JSXExpressionContainer)(node)) return getReportDescriptor(node.expression, visited);
2648
+ if (Check.is(AST_NODE_TYPES.JSXExpressionContainer)(node)) return getReportDescriptor(node.expression, seen);
2687
2649
  if (Check.isJSX(node)) return null;
2688
- if (Check.isTypeExpression(node)) return getReportDescriptor(node.expression, visited);
2650
+ if (Check.isTypeExpression(node)) return getReportDescriptor(node.expression, seen);
2689
2651
  return match(node).with({
2690
2652
  type: AST_NODE_TYPES.LogicalExpression,
2691
2653
  operator: "&&"
2692
2654
  }, ({ left, right }) => {
2693
- if (left.type === AST_NODE_TYPES.UnaryExpression && left.operator === "!") return getReportDescriptor(right, visited);
2655
+ if (left.type === AST_NODE_TYPES.UnaryExpression && left.operator === "!") return getReportDescriptor(right, seen);
2694
2656
  const initialScope = context.sourceCode.getScope(left);
2695
2657
  if (Check.isIdentifier(left, "NaN") || getStaticValue(left, initialScope)?.value === "NaN") return {
2696
2658
  data: { value: context.sourceCode.getText(left) },
@@ -2699,19 +2661,19 @@ function create$27(context) {
2699
2661
  };
2700
2662
  const leftType = getConstrainedTypeAtLocation(services, left);
2701
2663
  const leftTypeVariants = core.getTypeVariants(unionConstituents(leftType));
2702
- if (Array.from(leftTypeVariants.values()).every((t) => allowedTypeVariants.some((a) => a === t))) return getReportDescriptor(right, visited);
2664
+ if (Array.from(leftTypeVariants.values()).every((t) => allowedTypeVariants.some((a) => a === t))) return getReportDescriptor(right, seen);
2703
2665
  return {
2704
2666
  data: { value: context.sourceCode.getText(left) },
2705
2667
  messageId: "default",
2706
2668
  node: left
2707
2669
  };
2708
2670
  }).with({ type: AST_NODE_TYPES.ConditionalExpression }, ({ alternate, consequent }) => {
2709
- return getReportDescriptor(consequent, visited) ?? getReportDescriptor(alternate, visited);
2671
+ return getReportDescriptor(consequent, seen) ?? getReportDescriptor(alternate, seen);
2710
2672
  }).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
2711
- if (visited.has(n.name)) return null;
2712
- visited.add(n.name);
2673
+ if (seen.has(n.name)) return null;
2674
+ seen.add(n.name);
2713
2675
  const variableDefNode = findVariable(context.sourceCode.getScope(n), n.name)?.defs.at(0)?.node;
2714
- return match(variableDefNode).with({ init: P.select({ type: P.not(AST_NODE_TYPES.VariableDeclaration) }) }, (init) => getReportDescriptor(init, visited)).otherwise(() => null);
2676
+ return match(variableDefNode).with({ init: P.select({ type: P.not(AST_NODE_TYPES.VariableDeclaration) }) }, (init) => getReportDescriptor(init, seen)).otherwise(() => null);
2715
2677
  }).otherwise(() => null);
2716
2678
  }
2717
2679
  return { JSXExpressionContainer: flow(getReportDescriptor, report$1(context)) };
@@ -4207,11 +4169,11 @@ var purity_default = createRule({
4207
4169
  * or resolves to a non-builtin source.
4208
4170
  * @param context - The rule context.
4209
4171
  * @param node - The identifier node to resolve.
4210
- * @param visited - A set of already visited identifier names to prevent infinite loops.
4172
+ * @param seen - A set of already visited identifier names to prevent infinite loops.
4211
4173
  */
4212
- function resolveBuiltinObjectName(context, node, visited = /* @__PURE__ */ new Set()) {
4213
- if (visited.has(node.name)) return null;
4214
- visited.add(node.name);
4174
+ function resolveBuiltinObjectName(context, node, seen = /* @__PURE__ */ new Set()) {
4175
+ if (seen.has(node.name)) return null;
4176
+ seen.add(node.name);
4215
4177
  const variable = findVariable(context.sourceCode.getScope(node), node);
4216
4178
  if (variable == null) return node.name;
4217
4179
  const def = variable.defs[0];
@@ -4219,10 +4181,10 @@ function resolveBuiltinObjectName(context, node, visited = /* @__PURE__ */ new S
4219
4181
  if (def.type === DefinitionType.ImplicitGlobalVariable) return node.name;
4220
4182
  if (def.type === DefinitionType.Variable && def.node.init != null) {
4221
4183
  const init = Extract.unwrap(def.node.init);
4222
- if (init.type === AST_NODE_TYPES.Identifier) return resolveBuiltinObjectName(context, init, visited);
4184
+ if (init.type === AST_NODE_TYPES.Identifier) return resolveBuiltinObjectName(context, init, seen);
4223
4185
  if (init.type === AST_NODE_TYPES.MemberExpression) {
4224
4186
  const rootId = Extract.getRootIdentifier(init);
4225
- if (rootId != null) return resolveBuiltinObjectName(context, rootId, visited);
4187
+ if (rootId != null) return resolveBuiltinObjectName(context, rootId, seen);
4226
4188
  }
4227
4189
  }
4228
4190
  return null;
@@ -4395,9 +4357,9 @@ var refs_default = createRule({
4395
4357
  defaultOptions: []
4396
4358
  });
4397
4359
  function resolveAlias(name, aliases) {
4398
- const visited = /* @__PURE__ */ new Set();
4399
- while (aliases.has(name) && !visited.has(name)) {
4400
- visited.add(name);
4360
+ const seen = /* @__PURE__ */ new Set();
4361
+ while (aliases.has(name) && !seen.has(name)) {
4362
+ seen.add(name);
4401
4363
  name = aliases.get(name);
4402
4364
  }
4403
4365
  return name;
@@ -4544,7 +4506,7 @@ function create$6(context) {
4544
4506
  let matchedGuardIf = false;
4545
4507
  if (!isLazyInit && refName != null) {
4546
4508
  let current = node.parent;
4547
- findIf: for (;;) {
4509
+ findLoop: while (true) {
4548
4510
  if (current.type === AST_NODE_TYPES.IfStatement) {
4549
4511
  if (isRefCurrentNullCheck(current.test, refName)) {
4550
4512
  isLazyInit = true;
@@ -4566,7 +4528,7 @@ function create$6(context) {
4566
4528
  case AST_NODE_TYPES.MemberExpression:
4567
4529
  case AST_NODE_TYPES.ChainExpression:
4568
4530
  case AST_NODE_TYPES.CallExpression: break;
4569
- default: break findIf;
4531
+ default: break findLoop;
4570
4532
  }
4571
4533
  current = current.parent;
4572
4534
  }
@@ -5847,7 +5809,7 @@ var CodePath = class {
5847
5809
  let index = 0;
5848
5810
  let end = 0;
5849
5811
  let segment = null;
5850
- const visited = Object.create(null);
5812
+ const seen = Object.create(null);
5851
5813
  const stack = [[startSegment, 0]];
5852
5814
  let skippedSegment = null;
5853
5815
  let broken = false;
@@ -5861,14 +5823,14 @@ var CodePath = class {
5861
5823
  }
5862
5824
  };
5863
5825
  function isVisited(prevSegment) {
5864
- return visited[prevSegment.id] || segment.isLoopedPrevSegment(prevSegment);
5826
+ return seen[prevSegment.id] || segment.isLoopedPrevSegment(prevSegment);
5865
5827
  }
5866
5828
  while (stack.length > 0) {
5867
5829
  item = stack[stack.length - 1];
5868
5830
  segment = item[0];
5869
5831
  index = item[1];
5870
5832
  if (index === 0) {
5871
- if (visited[segment.id]) {
5833
+ if (seen[segment.id]) {
5872
5834
  stack.pop();
5873
5835
  continue;
5874
5836
  }
@@ -5877,7 +5839,7 @@ var CodePath = class {
5877
5839
  continue;
5878
5840
  }
5879
5841
  if (skippedSegment && segment.prevSegments.includes(skippedSegment)) skippedSegment = null;
5880
- visited[segment.id] = true;
5842
+ seen[segment.id] = true;
5881
5843
  if (!skippedSegment) {
5882
5844
  resolvedCallback.call(this, segment, controller);
5883
5845
  if (segment === lastSegment) controller.skip();
@@ -6866,9 +6828,9 @@ function isHookDecl(node) {
6866
6828
  default: return false;
6867
6829
  }
6868
6830
  }
6869
- function isInitializedFromRef(context, name, initialScope, visited = /* @__PURE__ */ new Set()) {
6870
- if (visited.has(name)) return false;
6871
- visited.add(name);
6831
+ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ */ new Set()) {
6832
+ if (seen.has(name)) return false;
6833
+ seen.add(name);
6872
6834
  for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
6873
6835
  if (node.type !== AST_NODE_TYPES.VariableDeclarator) continue;
6874
6836
  const init = node.init;
@@ -6876,7 +6838,7 @@ function isInitializedFromRef(context, name, initialScope, visited = /* @__PURE_
6876
6838
  switch (true) {
6877
6839
  case init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier && (init.object.name === "ref" || init.object.name.endsWith("Ref")): return true;
6878
6840
  case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefCall(context, init): return true;
6879
- case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), visited));
6841
+ case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
6880
6842
  }
6881
6843
  }
6882
6844
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.11.3",
3
+ "version": "5.12.0-beta.0",
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,12 +45,12 @@
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.11.3",
49
- "@eslint-react/shared": "5.11.3",
50
- "@eslint-react/var": "5.11.3",
51
- "@eslint-react/jsx": "5.11.3",
52
- "@eslint-react/core": "5.11.3",
53
- "@eslint-react/eslint": "5.11.3"
48
+ "@eslint-react/ast": "5.12.0-beta.0",
49
+ "@eslint-react/eslint": "5.12.0-beta.0",
50
+ "@eslint-react/shared": "5.12.0-beta.0",
51
+ "@eslint-react/jsx": "5.12.0-beta.0",
52
+ "@eslint-react/core": "5.12.0-beta.0",
53
+ "@eslint-react/var": "5.12.0-beta.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/react": "^19.2.17",
@@ -63,8 +63,8 @@
63
63
  "tsl": "^1.0.30",
64
64
  "tsl-dx": "^0.13.2",
65
65
  "typescript": "6.0.3",
66
- "@local/configs": "0.0.0",
67
- "@local/eff": "0.0.0"
66
+ "@local/eff": "0.0.0",
67
+ "@local/configs": "0.0.0"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "eslint": "*",