eslint-plugin-react-x 5.23.3 → 5.23.5

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 +66 -29
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -123,7 +123,8 @@ const rules$7 = {
123
123
  "react-x/no-unused-props": "off",
124
124
  "react-x/no-unused-state": "off",
125
125
  "react-x/refs": "off",
126
- "react-x/set-state-in-render": "off"
126
+ "react-x/set-state-in-render": "off",
127
+ "react-x/static-components": "off"
127
128
  };
128
129
 
129
130
  //#endregion
@@ -144,7 +145,7 @@ const rules$6 = {
144
145
  //#endregion
145
146
  //#region package.json
146
147
  var name$6 = "eslint-plugin-react-x";
147
- var version = "5.23.3";
148
+ var version = "5.23.5";
148
149
 
149
150
  //#endregion
150
151
  //#region src/utils/create-rule.ts
@@ -7979,15 +7980,29 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
7979
7980
  const { additionalRefHooks } = getSettingsFromContext(context);
7980
7981
  if (seen.has(name)) return false;
7981
7982
  seen.add(name);
7982
- for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
7983
+ for (const def of findVariable(initialScope, name)?.defs ?? []) {
7984
+ if (def.type === DefinitionType.Parameter) {
7985
+ if (def.name.type === AST_NODE_TYPES.Identifier && (def.name.name === "ref" || def.name.name.endsWith("Ref"))) return true;
7986
+ continue;
7987
+ }
7988
+ const { node } = def;
7983
7989
  if (node.type !== AST_NODE_TYPES.VariableDeclarator) continue;
7984
7990
  const init = node.init == null ? null : Extract.unwrap(node.init);
7985
7991
  if (init == null) continue;
7986
7992
  switch (true) {
7987
7993
  case init.type === AST_NODE_TYPES.MemberExpression: {
7988
- let object = Extract.unwrap(init.object);
7989
- while (object.type === AST_NODE_TYPES.MemberExpression) object = Extract.unwrap(object.object);
7990
- return Check.isIdentifier(object) && (object.name === "ref" || object.name.endsWith("Ref"));
7994
+ let current = init;
7995
+ while (current.type === AST_NODE_TYPES.MemberExpression) {
7996
+ const property = Extract.unwrap(current.property);
7997
+ const object = Extract.unwrap(current.object);
7998
+ if (!current.computed && Check.isIdentifier(property, "current")) {
7999
+ const sourceName = getRefSourceName(object);
8000
+ if (sourceName === "ref" || sourceName?.endsWith("Ref") === true) return true;
8001
+ }
8002
+ current = object;
8003
+ }
8004
+ if (Check.isIdentifier(current) && (current.name === "ref" || current.name.endsWith("Ref"))) return true;
8005
+ return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
7991
8006
  }
7992
8007
  case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefLikeCall(init, additionalRefHooks): return true;
7993
8008
  default: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
@@ -7996,6 +8011,21 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
7996
8011
  return false;
7997
8012
  }
7998
8013
  /**
8014
+ * Get the name that identifies the source of a `.current` member access.
8015
+ * @param node The object of the member expression
8016
+ * @returns The identifier name, the last property name of a member chain, or null
8017
+ */
8018
+ function getRefSourceName(node) {
8019
+ switch (node.type) {
8020
+ case AST_NODE_TYPES.Identifier: return node.name;
8021
+ case AST_NODE_TYPES.MemberExpression: {
8022
+ const property = Extract.unwrap(node.property);
8023
+ return !node.computed && property.type === AST_NODE_TYPES.Identifier ? property.name : null;
8024
+ }
8025
+ default: return null;
8026
+ }
8027
+ }
8028
+ /**
7999
8029
  * Check if a setState call is inside a conditional block whose test expression
8000
8030
  * is derived from a ref value (e.g. `if (prevRef.current !== value) setState(...)`).
8001
8031
  * @param context The ESLint rule context
@@ -8042,7 +8072,7 @@ function isRefInExpression(context, node) {
8042
8072
  * @returns The actual CallExpression node
8043
8073
  */
8044
8074
  function getSetStateCallExpression(node) {
8045
- return Check.isIdentifier(node) && node.parent.type === AST_NODE_TYPES.CallExpression ? node.parent : node;
8075
+ return Check.isIdentifier(node) && node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node ? node.parent : node;
8046
8076
  }
8047
8077
 
8048
8078
  //#endregion
@@ -8138,6 +8168,25 @@ function create$5(context) {
8138
8168
  default: return false;
8139
8169
  }
8140
8170
  }
8171
+ function isArgumentUsingRefValue(context, node) {
8172
+ const isUsingRefValue = (n) => {
8173
+ switch (n.type) {
8174
+ case AST_NODE_TYPES.Identifier: return isInitializedFromRef(context, n.name, context.sourceCode.getScope(n));
8175
+ case AST_NODE_TYPES.MemberExpression: return isUsingRefValue(n.object);
8176
+ case AST_NODE_TYPES.CallExpression: return isUsingRefValue(n.callee) || getNestedIdentifiers(n).some(isUsingRefValue);
8177
+ case AST_NODE_TYPES.BinaryExpression:
8178
+ case AST_NODE_TYPES.LogicalExpression: return isUsingRefValue(n.left) || isUsingRefValue(n.right);
8179
+ case AST_NODE_TYPES.UnaryExpression:
8180
+ case AST_NODE_TYPES.UpdateExpression: return isUsingRefValue(n.argument);
8181
+ case AST_NODE_TYPES.ConditionalExpression: return isUsingRefValue(n.consequent) || isUsingRefValue(n.alternate);
8182
+ case AST_NODE_TYPES.SequenceExpression: return n.expressions.some(isUsingRefValue);
8183
+ case AST_NODE_TYPES.AssignmentExpression: return isUsingRefValue(n.right);
8184
+ default: return false;
8185
+ }
8186
+ };
8187
+ if (isUsingRefValue(node)) return true;
8188
+ return Check.isFunction(node) && context.sourceCode.getScope(node.body).references.some((r) => isUsingRefValue(r.identifier));
8189
+ }
8141
8190
  return {
8142
8191
  ":function"(node) {
8143
8192
  const kind = getFunctionKind(node);
@@ -8164,25 +8213,6 @@ function create$5(context) {
8164
8213
  case entry.kind === "immediate" && Traverse.findParent(entry.node, Check.isFunction) === setupFunction: {
8165
8214
  const args0 = node.arguments.at(0);
8166
8215
  if (args0 == null) return;
8167
- function isArgumentUsingRefValue(context, node) {
8168
- const isUsingRefValue = (n) => {
8169
- switch (n.type) {
8170
- case AST_NODE_TYPES.Identifier: return isInitializedFromRef(context, n.name, context.sourceCode.getScope(n));
8171
- case AST_NODE_TYPES.MemberExpression: return isUsingRefValue(n.object);
8172
- case AST_NODE_TYPES.CallExpression: return isUsingRefValue(n.callee) || getNestedIdentifiers(n).some(isUsingRefValue);
8173
- case AST_NODE_TYPES.BinaryExpression:
8174
- case AST_NODE_TYPES.LogicalExpression: return isUsingRefValue(n.left) || isUsingRefValue(n.right);
8175
- case AST_NODE_TYPES.UnaryExpression:
8176
- case AST_NODE_TYPES.UpdateExpression: return isUsingRefValue(n.argument);
8177
- case AST_NODE_TYPES.ConditionalExpression: return isUsingRefValue(n.consequent) || isUsingRefValue(n.alternate);
8178
- case AST_NODE_TYPES.SequenceExpression: return n.expressions.some(isUsingRefValue);
8179
- case AST_NODE_TYPES.AssignmentExpression: return isUsingRefValue(n.right);
8180
- default: return false;
8181
- }
8182
- };
8183
- if (isUsingRefValue(node)) return true;
8184
- return Check.isFunction(node) && context.sourceCode.getScope(node.body).references.some((r) => isUsingRefValue(r.identifier));
8185
- }
8186
8216
  if (isArgumentUsingRefValue(context, args0)) return;
8187
8217
  if (isRefGatedContext(context, node)) return;
8188
8218
  context.report({
@@ -8229,6 +8259,13 @@ function create$5(context) {
8229
8259
  }
8230
8260
  },
8231
8261
  "Program:exit"() {
8262
+ const isExemptSetStateCall = (setStateCall) => {
8263
+ const callNode = getSetStateCallExpression(setStateCall);
8264
+ if (isRefGatedContext(context, callNode)) return true;
8265
+ if (callNode.type !== AST_NODE_TYPES.CallExpression) return false;
8266
+ const args0 = callNode.arguments.at(0);
8267
+ return args0 != null && isArgumentUsingRefValue(context, args0);
8268
+ };
8232
8269
  const getSetStateCalls = (context, id) => {
8233
8270
  const variable = findVariable(context.sourceCode.getScope(id), id);
8234
8271
  if (variable != null && variable.defs.some((def) => def.type === DefinitionType.Parameter)) return [];
@@ -8242,7 +8279,7 @@ function create$5(context) {
8242
8279
  return [];
8243
8280
  };
8244
8281
  for (const [, calls] of setStateInEffectSetup) for (const call of calls) {
8245
- if (isRefGatedContext(context, getSetStateCallExpression(call))) continue;
8282
+ if (isExemptSetStateCall(call)) continue;
8246
8283
  context.report({
8247
8284
  data: { name: call.name },
8248
8285
  messageId: "default",
@@ -8254,7 +8291,7 @@ function create$5(context) {
8254
8291
  if (!Check.isIdentifier(unwrappedCallee)) continue;
8255
8292
  const setStateCalls = getSetStateCalls(context, unwrappedCallee);
8256
8293
  for (const setStateCall of setStateCalls) {
8257
- if (isRefGatedContext(context, getSetStateCallExpression(setStateCall))) continue;
8294
+ if (isExemptSetStateCall(setStateCall)) continue;
8258
8295
  context.report({
8259
8296
  data: { name: getCallName(setStateCall) },
8260
8297
  messageId: "default",
@@ -8265,7 +8302,7 @@ function create$5(context) {
8265
8302
  for (const id of setupFnIds) {
8266
8303
  const setStateCalls = getSetStateCalls(context, id);
8267
8304
  for (const setStateCall of setStateCalls) {
8268
- if (isRefGatedContext(context, getSetStateCallExpression(setStateCall))) continue;
8305
+ if (isExemptSetStateCall(setStateCall)) continue;
8269
8306
  context.report({
8270
8307
  data: { name: getCallName(setStateCall) },
8271
8308
  messageId: "default",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.23.3",
3
+ "version": "5.23.5",
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",
@@ -36,12 +36,12 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@eslint-react/ast": "5.23.3",
40
- "@eslint-react/core": "5.23.3",
41
- "@eslint-react/eslint": "5.23.3",
42
- "@eslint-react/jsx": "5.23.3",
43
- "@eslint-react/shared": "5.23.3",
44
- "@eslint-react/var": "5.23.3",
39
+ "@eslint-react/ast": "5.23.5",
40
+ "@eslint-react/core": "5.23.5",
41
+ "@eslint-react/eslint": "5.23.5",
42
+ "@eslint-react/jsx": "5.23.5",
43
+ "@eslint-react/shared": "5.23.5",
44
+ "@eslint-react/var": "5.23.5",
45
45
  "@typescript-eslint/scope-manager": "^8.71.0",
46
46
  "@typescript-eslint/types": "^8.71.0",
47
47
  "@typescript-eslint/typescript-estree": "^8.71.0",