eslint-plugin-react-x 5.5.4-beta.0 → 5.5.5-beta.1

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 +68 -16
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -142,7 +142,7 @@ const rules$6 = {
142
142
  //#endregion
143
143
  //#region package.json
144
144
  var name$6 = "eslint-plugin-react-x";
145
- var version = "5.5.4-beta.0";
145
+ var version = "5.5.5-beta.1";
146
146
 
147
147
  //#endregion
148
148
  //#region src/utils/create-rule.ts
@@ -6584,10 +6584,45 @@ function isInitializedFromRef(context, name, initialScope) {
6584
6584
  switch (true) {
6585
6585
  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;
6586
6586
  case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefCall(context, init): return true;
6587
+ case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id)));
6587
6588
  }
6588
6589
  }
6589
6590
  return false;
6590
6591
  }
6592
+ /**
6593
+ * Check if a setState call is inside a conditional block whose test expression
6594
+ * is derived from a ref value (e.g. `if (prevRef.current !== value) setState(...)`).
6595
+ * @param context The ESLint rule context
6596
+ * @param node The AST node to check
6597
+ * @returns `true` if the node is inside a ref-gated conditional block
6598
+ */
6599
+ function isRefGatedContext(context, node) {
6600
+ let current = node.parent;
6601
+ while (current != null) {
6602
+ if (Check.isFunction(current)) break;
6603
+ if (current.type === AST_NODE_TYPES.IfStatement) {
6604
+ if (isRefInExpression(context, current.test)) return true;
6605
+ }
6606
+ if (current.type === AST_NODE_TYPES.ConditionalExpression) {
6607
+ if (isRefInExpression(context, current.test)) return true;
6608
+ }
6609
+ current = current.parent;
6610
+ }
6611
+ return false;
6612
+ }
6613
+ function isRefInExpression(context, node) {
6614
+ return getNestedIdentifiers(node).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id)));
6615
+ }
6616
+ /**
6617
+ * Get the actual CallExpression node from a setState call reference.
6618
+ * When the node is an Identifier that is the callee of a CallExpression,
6619
+ * returns the parent CallExpression; otherwise returns the node itself.
6620
+ * @param node The setState call node (CallExpression or Identifier)
6621
+ * @returns The actual CallExpression node
6622
+ */
6623
+ function getSetStateCallExpression(node) {
6624
+ return node.type === AST_NODE_TYPES.Identifier && node.parent?.type === AST_NODE_TYPES.CallExpression ? node.parent : node;
6625
+ }
6591
6626
 
6592
6627
  //#endregion
6593
6628
  //#region src/rules/set-state-in-effect/set-state-in-effect.ts
@@ -6711,6 +6746,13 @@ function create$5(context) {
6711
6746
  case AST_NODE_TYPES.Identifier: return isInitializedFromRef(context, n.name, context.sourceCode.getScope(n));
6712
6747
  case AST_NODE_TYPES.MemberExpression: return isUsingRefValue(n.object);
6713
6748
  case AST_NODE_TYPES.CallExpression: return isUsingRefValue(n.callee) || getNestedIdentifiers(n).some(isUsingRefValue);
6749
+ case AST_NODE_TYPES.BinaryExpression:
6750
+ case AST_NODE_TYPES.LogicalExpression: return isUsingRefValue(n.left) || isUsingRefValue(n.right);
6751
+ case AST_NODE_TYPES.UnaryExpression:
6752
+ case AST_NODE_TYPES.UpdateExpression: return isUsingRefValue(n.argument);
6753
+ case AST_NODE_TYPES.ConditionalExpression: return isUsingRefValue(n.consequent) || isUsingRefValue(n.alternate);
6754
+ case AST_NODE_TYPES.SequenceExpression: return n.expressions.some(isUsingRefValue);
6755
+ case AST_NODE_TYPES.AssignmentExpression: return isUsingRefValue(n.right);
6714
6756
  default: return false;
6715
6757
  }
6716
6758
  };
@@ -6718,6 +6760,7 @@ function create$5(context) {
6718
6760
  return Check.isFunction(node) && context.sourceCode.getScope(node.body).references.some((r) => isUsingRefValue(r.identifier));
6719
6761
  }
6720
6762
  if (isArgumentUsingRefValue(context, args0)) return;
6763
+ if (isRefGatedContext(context, node)) return;
6721
6764
  context.report({
6722
6765
  data: { name: context.sourceCode.getText(node.callee) },
6723
6766
  messageId: "default",
@@ -6772,27 +6815,36 @@ function create$5(context) {
6772
6815
  }
6773
6816
  return [];
6774
6817
  };
6775
- for (const [, calls] of setStateInEffectSetup) for (const call of calls) context.report({
6776
- data: { name: call.name },
6777
- messageId: "default",
6778
- node: call
6779
- });
6818
+ for (const [, calls] of setStateInEffectSetup) for (const call of calls) {
6819
+ if (isRefGatedContext(context, getSetStateCallExpression(call))) continue;
6820
+ context.report({
6821
+ data: { name: call.name },
6822
+ messageId: "default",
6823
+ node: call
6824
+ });
6825
+ }
6780
6826
  for (const { callee } of trackedFnCalls) {
6781
6827
  if (!("name" in callee)) continue;
6782
6828
  const setStateCalls = getSetStateCalls(context, callee);
6783
- for (const setStateCall of setStateCalls) context.report({
6784
- data: { name: getCallName(setStateCall) },
6785
- messageId: "default",
6786
- node: setStateCall
6787
- });
6829
+ for (const setStateCall of setStateCalls) {
6830
+ if (isRefGatedContext(context, getSetStateCallExpression(setStateCall))) continue;
6831
+ context.report({
6832
+ data: { name: getCallName(setStateCall) },
6833
+ messageId: "default",
6834
+ node: setStateCall
6835
+ });
6836
+ }
6788
6837
  }
6789
6838
  for (const id of setupFnIds) {
6790
6839
  const setStateCalls = getSetStateCalls(context, id);
6791
- for (const setStateCall of setStateCalls) context.report({
6792
- data: { name: getCallName(setStateCall) },
6793
- messageId: "default",
6794
- node: setStateCall
6795
- });
6840
+ for (const setStateCall of setStateCalls) {
6841
+ if (isRefGatedContext(context, getSetStateCallExpression(setStateCall))) continue;
6842
+ context.report({
6843
+ data: { name: getCallName(setStateCall) },
6844
+ messageId: "default",
6845
+ node: setStateCall
6846
+ });
6847
+ }
6796
6848
  }
6797
6849
  }
6798
6850
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.5.4-beta.0",
3
+ "version": "5.5.5-beta.1",
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.5.4-beta.0",
49
- "@eslint-react/core": "5.5.4-beta.0",
50
- "@eslint-react/eslint": "5.5.4-beta.0",
51
- "@eslint-react/jsx": "5.5.4-beta.0",
52
- "@eslint-react/var": "5.5.4-beta.0",
53
- "@eslint-react/shared": "5.5.4-beta.0"
48
+ "@eslint-react/ast": "5.5.5-beta.1",
49
+ "@eslint-react/eslint": "5.5.5-beta.1",
50
+ "@eslint-react/jsx": "5.5.5-beta.1",
51
+ "@eslint-react/shared": "5.5.5-beta.1",
52
+ "@eslint-react/var": "5.5.5-beta.1",
53
+ "@eslint-react/core": "5.5.5-beta.1"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/react": "^19.2.14",