eslint-plugin-react-x 5.5.1-next.0 → 5.5.2-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 +59 -13
  2. package/package.json +11 -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.1-next.0";
145
+ var version = "5.5.2-beta.0";
146
146
 
147
147
  //#endregion
148
148
  //#region src/utils/create-rule.ts
@@ -2726,7 +2726,7 @@ function report(context) {
2726
2726
  * @param node The AST node
2727
2727
  * @returns The nested return statements in the node
2728
2728
  */
2729
- function getNestedReturnStatements(node) {
2729
+ function getNestedReturnStatements$1(node) {
2730
2730
  const statements = [];
2731
2731
  const boundaryNode = Check.isFunction(node) ? node : Traverse.findParent(node, Check.isFunction);
2732
2732
  simpleTraverse(node, { enter(node) {
@@ -2778,7 +2778,7 @@ function create$23(context) {
2778
2778
  }
2779
2779
  function checkBlock(node) {
2780
2780
  const descriptors = [];
2781
- for (const stmt of getNestedReturnStatements(node)) {
2781
+ for (const stmt of getNestedReturnStatements$1(node)) {
2782
2782
  if (stmt.argument == null) continue;
2783
2783
  const desc = check(stmt.argument);
2784
2784
  if (desc == null) continue;
@@ -7115,6 +7115,32 @@ function create$2(context) {
7115
7115
  });
7116
7116
  }
7117
7117
 
7118
+ //#endregion
7119
+ //#region src/rules/use-memo/lib.ts
7120
+ function isInsideNestedFunction(node, boundary) {
7121
+ let current = node.parent;
7122
+ while (current && current !== boundary) {
7123
+ if (Check.isFunction(current)) return true;
7124
+ current = current.parent;
7125
+ }
7126
+ return false;
7127
+ }
7128
+ /**
7129
+ * Gets the nested return statements in the node that are within the same function
7130
+ * @param node The AST node
7131
+ * @returns The nested return statements in the node
7132
+ */
7133
+ function getNestedReturnStatements(node) {
7134
+ const statements = [];
7135
+ const boundaryNode = Check.isFunction(node) ? node : Traverse.findParent(node, Check.isFunction);
7136
+ simpleTraverse(node, { enter(node) {
7137
+ if (node.type !== AST_NODE_TYPES.ReturnStatement) return;
7138
+ if (Traverse.findParent(node, Check.isFunction) !== boundaryNode) return;
7139
+ statements.push(node);
7140
+ } });
7141
+ return statements;
7142
+ }
7143
+
7118
7144
  //#endregion
7119
7145
  //#region src/rules/use-memo/use-memo.ts
7120
7146
  const RULE_NAME$1 = "use-memo";
@@ -7123,10 +7149,11 @@ var use_memo_default = createRule({
7123
7149
  type: "problem",
7124
7150
  docs: { description: "Validates that 'useMemo' is called with a callback that returns a value." },
7125
7151
  messages: {
7126
- asyncOrGeneratorCallback: "The callback passed to 'useMemo' must be a regular function. 'useMemo' callbacks are called synchronously by React and must immediately return a value. Async and generator functions are not supported.",
7127
- callbackWithParameters: "The callback passed to 'useMemo' may not accept parameters. 'useMemo' callbacks are called by React without arguments. Instead, directly reference the props, state, or local variables needed for the computation.",
7128
- missingReturnValue: "The callback passed to 'useMemo' must return a value. Without a return value, 'useMemo' always returns 'undefined', which defeats its purpose.",
7129
- notAssignedToVariable: "The return value of 'useMemo' must be assigned to a variable. Calling 'useMemo' without capturing its return value is likely a mistake use 'useEffect' for side effects instead."
7152
+ mustReturnAValue: "useMemo() callbacks must return a value. This useMemo() callback doesn't return a value. useMemo() is for computing and caching values, not for arbitrary side effects.",
7153
+ noAsyncOrGeneratorFunctions: "useMemo() callbacks may not be async or generator functions. useMemo() callbacks are called once and must synchronously return a value.",
7154
+ noParameters: "useMemo() callbacks may not accept parameters. useMemo() callbacks are called by React to cache calculations across re-renders. They should not take parameters. Instead, directly reference the props, state, or local variables needed for the computation.",
7155
+ noReassigningOuterVariables: "useMemo() callbacks may not reassign variables declared outside of the callback. useMemo() callbacks must be pure functions and cannot reassign variables defined outside of the callback function.",
7156
+ resultMustBeUsed: "useMemo() result is unused. This useMemo() value is unused. useMemo() is for computing and caching values, not for arbitrary side effects."
7130
7157
  },
7131
7158
  schema: []
7132
7159
  },
@@ -7136,13 +7163,31 @@ var use_memo_default = createRule({
7136
7163
  });
7137
7164
  function create$1(context) {
7138
7165
  if (!context.sourceCode.text.includes("useMemo")) return {};
7166
+ function validateNoOuterVariableReassignment(callback) {
7167
+ const violations = [];
7168
+ const callbackScope = context.sourceCode.getScope(callback);
7169
+ const localVars = new Set(callbackScope.variables.map((v) => v.name));
7170
+ if (callback.body == null) return violations;
7171
+ simpleTraverse(callback.body, { enter(node) {
7172
+ if (node.type !== AST_NODE_TYPES.AssignmentExpression) return;
7173
+ const left = Extract.unwrap(node.left);
7174
+ if (left.type !== AST_NODE_TYPES.Identifier) return;
7175
+ if (localVars.has(left.name)) return;
7176
+ if (isInsideNestedFunction(node, callback)) return;
7177
+ violations.push({
7178
+ messageId: "noReassigningOuterVariables",
7179
+ node: left
7180
+ });
7181
+ } });
7182
+ return violations;
7183
+ }
7139
7184
  return merge({ CallExpression(node) {
7140
7185
  if (!core.isUseMemoCall(context, node)) return;
7141
7186
  let parent = node.parent;
7142
7187
  while (Check.isTypeExpression(parent)) parent = parent.parent;
7143
7188
  if (!(parent.type === AST_NODE_TYPES.VariableDeclarator || parent.type === AST_NODE_TYPES.AssignmentExpression || parent.type === AST_NODE_TYPES.AssignmentPattern || parent.type === AST_NODE_TYPES.Property || parent.type === AST_NODE_TYPES.ReturnStatement || parent.type === AST_NODE_TYPES.JSXExpressionContainer || parent.type === AST_NODE_TYPES.CallExpression || parent.type === AST_NODE_TYPES.NewExpression || parent.type === AST_NODE_TYPES.ArrayExpression || parent.type === AST_NODE_TYPES.ConditionalExpression || parent.type === AST_NODE_TYPES.LogicalExpression || parent.type === AST_NODE_TYPES.SequenceExpression || parent.type === AST_NODE_TYPES.SpreadElement || parent.type === AST_NODE_TYPES.TemplateLiteral || parent.type === AST_NODE_TYPES.BinaryExpression || parent.type === AST_NODE_TYPES.UnaryExpression || parent.type === AST_NODE_TYPES.MemberExpression || parent.type === AST_NODE_TYPES.TaggedTemplateExpression || parent.type === AST_NODE_TYPES.ChainExpression || parent.type === AST_NODE_TYPES.ArrowFunctionExpression)) {
7144
7189
  context.report({
7145
- messageId: "notAssignedToVariable",
7190
+ messageId: "resultMustBeUsed",
7146
7191
  node
7147
7192
  });
7148
7193
  return;
@@ -7155,26 +7200,27 @@ function create$1(context) {
7155
7200
  const firstParam = callback.params[0];
7156
7201
  if (firstParam == null) return;
7157
7202
  context.report({
7158
- messageId: "callbackWithParameters",
7203
+ messageId: "noParameters",
7159
7204
  node: firstParam.type === AST_NODE_TYPES.Identifier ? firstParam : callback
7160
7205
  });
7161
7206
  }
7162
7207
  if (callback.async || callback.generator) context.report({
7163
- messageId: "asyncOrGeneratorCallback",
7208
+ messageId: "noAsyncOrGeneratorFunctions",
7164
7209
  node: callback
7165
7210
  });
7211
+ for (const violation of validateNoOuterVariableReassignment(callback)) context.report(violation);
7166
7212
  if (callback.type === AST_NODE_TYPES.ArrowFunctionExpression && callback.body.type !== AST_NODE_TYPES.BlockStatement) return;
7167
7213
  if (callback.body.type !== AST_NODE_TYPES.BlockStatement) return;
7168
- const returnStatements = getNestedReturnStatements(callbackArg);
7214
+ const returnStatements = getNestedReturnStatements(callback);
7169
7215
  if (returnStatements.length === 0) {
7170
7216
  context.report({
7171
- messageId: "missingReturnValue",
7217
+ messageId: "mustReturnAValue",
7172
7218
  node: callbackArg
7173
7219
  });
7174
7220
  return;
7175
7221
  }
7176
7222
  if (!returnStatements.some((stmt) => stmt.argument != null)) context.report({
7177
- messageId: "missingReturnValue",
7223
+ messageId: "mustReturnAValue",
7178
7224
  node: callbackArg
7179
7225
  });
7180
7226
  } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.5.1-next.0",
3
+ "version": "5.5.2-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",
@@ -46,18 +46,22 @@
46
46
  "string-ts": "^2.3.1",
47
47
  "ts-api-utils": "^2.5.0",
48
48
  "ts-pattern": "^5.9.0",
49
- "@eslint-react/ast": "5.5.1-next.0",
50
- "@eslint-react/core": "5.5.1-next.0",
51
- "@eslint-react/eslint": "5.5.1-next.0",
52
- "@eslint-react/jsx": "5.5.1-next.0",
53
- "@eslint-react/shared": "5.5.1-next.0",
54
- "@eslint-react/var": "5.5.1-next.0"
49
+ "@eslint-react/core": "5.5.2-beta.0",
50
+ "@eslint-react/jsx": "5.5.2-beta.0",
51
+ "@eslint-react/var": "5.5.2-beta.0",
52
+ "@eslint-react/shared": "5.5.2-beta.0",
53
+ "@eslint-react/eslint": "5.5.2-beta.0",
54
+ "@eslint-react/ast": "5.5.2-beta.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/react": "^19.2.14",
58
58
  "@types/react-dom": "^19.2.3",
59
+ "dedent": "^1.7.2",
59
60
  "eslint": "^10.2.1",
61
+ "react": "^19.2.5",
62
+ "react-dom": "^19.2.5",
60
63
  "tsdown": "^0.21.10",
64
+ "tsl": "^1.0.30",
61
65
  "tsl-dx": "^0.12.0",
62
66
  "@local/configs": "0.0.0",
63
67
  "@local/eff": "3.0.0-beta.72"