eslint-plugin-react-x 5.21.0 → 5.21.2

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 +75 -51
  2. package/package.json +7 -7
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.21.0";
147
+ var version = "5.21.2";
148
148
 
149
149
  //#endregion
150
150
  //#region src/utils/create-rule.ts
@@ -1807,7 +1807,7 @@ function create$47(context) {
1807
1807
  if (!context.sourceCode.text.includes("setState")) return {};
1808
1808
  return { MemberExpression(node) {
1809
1809
  if (!core.isAPI("this.state")(context, node)) return;
1810
- const fact = Traverse.findParent(Traverse.findParent(node, (n) => Check.is(AST_NODE_TYPES.CallExpression)(n) && core.isThisSetStateCall(n)), (n) => Check.isClass(n));
1810
+ const fact = Traverse.findParent(Traverse.findParent(node, (n) => Check.isCallExpression(n) && core.isThisSetStateCall(n)), Check.isClass);
1811
1811
  if (fact == null || !core.isClassComponent(fact)) return;
1812
1812
  context.report({
1813
1813
  messageId: "default",
@@ -2329,7 +2329,7 @@ var no_direct_mutation_state_default = createRule({
2329
2329
  defaultOptions: []
2330
2330
  });
2331
2331
  function isConstructorFunction(node) {
2332
- return Check.isOneOf([AST_NODE_TYPES.FunctionDeclaration, AST_NODE_TYPES.FunctionExpression])(node) && Check.isPropertyOrMethod(node.parent) && Check.isIdentifier(node.parent.key) && node.parent.key.name === "constructor";
2332
+ return Check.isOneOf([AST_NODE_TYPES.FunctionDeclaration, AST_NODE_TYPES.FunctionExpression])(node) && Check.isPropertyOrMethod(node.parent) && Check.isIdentifier(node.parent.key, "constructor");
2333
2333
  }
2334
2334
  function isAssignmentToThisState(node) {
2335
2335
  const { left } = node;
@@ -2345,7 +2345,7 @@ function isAssignmentToThisState(node) {
2345
2345
  function create$33(context) {
2346
2346
  return { AssignmentExpression(node) {
2347
2347
  if (!isAssignmentToThisState(node)) return;
2348
- const fact = Traverse.findParent(node, Check.isOneOf([AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.ClassExpression]));
2348
+ const fact = Traverse.findParent(node, Check.isClass);
2349
2349
  if (fact == null) return;
2350
2350
  if (core.isClassComponent(fact) && context.sourceCode.getScope(node).block !== Traverse.findParent(node, isConstructorFunction)) context.report({
2351
2351
  messageId: "default",
@@ -2397,7 +2397,8 @@ function create$32(context) {
2397
2397
  const call = Traverse.findParent(jsxElement, (n) => {
2398
2398
  if (n.type !== AST_NODE_TYPES.CallExpression) return false;
2399
2399
  const callee = Extract.unwrap(n.callee);
2400
- return callee.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(callee.property) && callee.property.name === "map";
2400
+ if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
2401
+ return Check.isIdentifier(callee.property, "map");
2401
2402
  });
2402
2403
  const iter = Traverse.findParent(jsxElement, Check.isFunction, (n) => n === call);
2403
2404
  if (!Check.isFunction(iter)) return;
@@ -3242,7 +3243,7 @@ function create$27(context) {
3242
3243
  */
3243
3244
  function visit(node, seen = /* @__PURE__ */ new Set()) {
3244
3245
  if (node == null) return null;
3245
- if (Check.is(AST_NODE_TYPES.JSXExpressionContainer)(node)) return visit(node.expression, seen);
3246
+ if (Check.isJSXExpressionContainer(node)) return visit(node.expression, seen);
3246
3247
  if (Check.isJSX(node)) return null;
3247
3248
  if (Check.isTypeExpression(node)) return visit(node.expression, seen);
3248
3249
  return match(node).with({
@@ -3560,8 +3561,8 @@ function create$23(context) {
3560
3561
  if (node.source.value !== importSource) return;
3561
3562
  for (const specifier of node.specifiers) {
3562
3563
  if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) continue;
3563
- if (specifier.imported.type !== AST_NODE_TYPES.Identifier) continue;
3564
- if (specifier.imported.name === "captureOwnerStack") context.report({
3564
+ if (!Check.isIdentifier(specifier.imported, "captureOwnerStack")) continue;
3565
+ context.report({
3565
3566
  messageId: "useNamespaceImport",
3566
3567
  node: specifier
3567
3568
  });
@@ -3906,15 +3907,22 @@ function create$18(context) {
3906
3907
 
3907
3908
  //#endregion
3908
3909
  //#region src/rules/no-unnecessary-use-prefix/lib.ts
3909
- const WELL_KNOWN_HOOKS = ["useMDXComponents"];
3910
+ const WELL_KNOWN_HOOKS = ["use", "useMDXComponents"];
3910
3911
  function containsUseComments(context, node) {
3911
3912
  return context.sourceCode.getCommentsInside(node).some(({ value }) => /use\([\s\S]*?\)/u.test(value) || /use[A-Z0-9]\w*\([\s\S]*?\)/u.test(value));
3912
3913
  }
3913
- function isTestMock(node) {
3914
- return node != null && node.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(node.object) && Check.isIdentifier(node.property) && node.property.name === "mock";
3914
+ function isTestMockCall(node) {
3915
+ if (node == null || node.type !== AST_NODE_TYPES.CallExpression) return false;
3916
+ const { callee } = node;
3917
+ if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
3918
+ if (Check.isIdentifier(callee.object) && Check.isIdentifier(callee.property, "mock")) return true;
3919
+ return Check.isIdentifier(callee.object, "mock") && Check.isIdentifier(callee.property, "module");
3915
3920
  }
3916
- function isTestMockCallback(node) {
3917
- return node != null && Check.isFunction(node) && node.parent.type === AST_NODE_TYPES.CallExpression && isTestMock(node.parent.callee) && node.parent.arguments[1] === node;
3921
+ function isInTestMock(node) {
3922
+ return Traverse.findParent(node, (n) => {
3923
+ if (!isTestMockCall(n)) return false;
3924
+ return n.arguments.slice(1).some((arg) => arg.range[0] <= node.range[0] && node.range[1] <= arg.range[1]);
3925
+ }) != null;
3918
3926
  }
3919
3927
 
3920
3928
  //#endregion
@@ -3940,7 +3948,7 @@ function create$17(context) {
3940
3948
  if (core.isFunctionEmpty(node)) continue;
3941
3949
  if (WELL_KNOWN_HOOKS.includes(name)) continue;
3942
3950
  if (containsUseComments(context, node)) continue;
3943
- if (Traverse.findParent(node, isTestMockCallback) != null) continue;
3951
+ if (isInTestMock(node)) continue;
3944
3952
  context.report({
3945
3953
  data: { name },
3946
3954
  messageId: "default",
@@ -4175,7 +4183,7 @@ function create$12(context, [options]) {
4175
4183
  if (safePatterns.length > 0) {
4176
4184
  const getName = (n) => Check.isIdentifier(n) ? n.name : null;
4177
4185
  const name = match(right).with({ type: AST_NODE_TYPES.NewExpression }, (n) => getName(n.callee)).with({ type: AST_NODE_TYPES.CallExpression }, (n) => {
4178
- return match(Extract.unwrap(n.callee)).when(Check.isIdentifier, ({ name }) => name).when(Check.is(AST_NODE_TYPES.MemberExpression), ({ object }) => getName(object)).otherwise(() => null);
4186
+ return match(Extract.unwrap(n.callee)).when(Check.isIdentifier, ({ name }) => name).when(Check.isMemberExpression, ({ object }) => getName(object)).otherwise(() => null);
4179
4187
  }).otherwise(() => null);
4180
4188
  if (name != null && safePatterns.some((pattern) => pattern.test(name))) continue;
4181
4189
  }
@@ -4214,6 +4222,14 @@ const LIFECYCLE_METHODS = /* @__PURE__ */ new Set([
4214
4222
  "UNSAFE_componentWillReceiveProps",
4215
4223
  "UNSAFE_componentWillUpdate"
4216
4224
  ]);
4225
+ const HOST_CONVENTION_METHODS = /* @__PURE__ */ new Set([
4226
+ "blur",
4227
+ "focus",
4228
+ "measure",
4229
+ "measureInWindow",
4230
+ "measureLayout",
4231
+ "setNativeProps"
4232
+ ]);
4217
4233
 
4218
4234
  //#endregion
4219
4235
  //#region src/rules/no-unused-class-component-members/no-unused-class-component-members.ts
@@ -4230,29 +4246,21 @@ var no_unused_class_component_members_default = createRule({
4230
4246
  defaultOptions: []
4231
4247
  });
4232
4248
  function create$11(context) {
4233
- const classStack = [];
4234
- const methodStack = [];
4235
4249
  const propertyDefs = /* @__PURE__ */ new WeakMap();
4236
4250
  const propertyUsages = /* @__PURE__ */ new WeakMap();
4237
- function classEnter(node) {
4238
- classStack.push(node);
4251
+ function classExit(node) {
4239
4252
  if (!core.isClassComponent(node)) return;
4240
- propertyDefs.set(node, /* @__PURE__ */ new Set());
4241
- propertyUsages.set(node, /* @__PURE__ */ new Set());
4242
- }
4243
- function classExit() {
4244
- const currentClass = classStack.pop();
4245
- if (currentClass == null || !core.isClassComponent(currentClass)) return;
4246
- const id = core.getClassId(currentClass);
4247
- const defs = propertyDefs.get(currentClass);
4248
- const usages = propertyUsages.get(currentClass);
4253
+ const id = core.getClassId(node);
4254
+ const defs = propertyDefs.get(node);
4249
4255
  if (defs == null) return;
4256
+ const usages = propertyUsages.get(node);
4250
4257
  for (const def of defs) {
4251
4258
  const methodName = def.name;
4252
4259
  if (usages?.has(methodName) ?? false) continue;
4253
4260
  if (LIFECYCLE_METHODS.has(methodName)) {
4254
- if (methodName === "shouldComponentUpdate" && core.isPureComponent(currentClass)) {} else continue;
4261
+ if (methodName === "shouldComponentUpdate" && core.isPureComponent(node)) {} else continue;
4255
4262
  }
4263
+ if (HOST_CONVENTION_METHODS.has(methodName)) continue;
4256
4264
  context.report({
4257
4265
  data: {
4258
4266
  className: id != null ? context.sourceCode.getText(id) : "Component",
@@ -4264,43 +4272,35 @@ function create$11(context) {
4264
4272
  }
4265
4273
  }
4266
4274
  function methodEnter(node) {
4267
- methodStack.push(node);
4268
- const currentClass = classStack.at(-1);
4275
+ const currentClass = Traverse.findParent(node, Check.isClass);
4269
4276
  if (currentClass == null || !core.isClassComponent(currentClass)) return;
4270
4277
  if (node.static) return;
4271
- if (!node.computed && node.key.type === AST_NODE_TYPES.Identifier) propertyDefs.get(currentClass)?.add(node.key);
4272
- }
4273
- function methodExit() {
4274
- methodStack.pop();
4278
+ if (!node.computed && node.key.type === AST_NODE_TYPES.Identifier) getOrInsertComputed(propertyDefs, currentClass, () => /* @__PURE__ */ new Set()).add(node.key);
4275
4279
  }
4276
4280
  return {
4277
- ClassDeclaration: classEnter,
4278
4281
  "ClassDeclaration:exit": classExit,
4279
- ClassExpression: classEnter,
4280
4282
  "ClassExpression:exit": classExit,
4281
4283
  MemberExpression(node) {
4282
- const currentClass = classStack.at(-1);
4283
- const currentMethod = methodStack.at(-1);
4284
+ const currentClass = Traverse.findParent(node, Check.isClass);
4285
+ const currentMethod = Traverse.findParent(node, Check.isPropertyOrMethod);
4284
4286
  if (currentClass == null || currentMethod == null) return;
4285
4287
  if (!core.isClassComponent(currentClass) || currentMethod.static) return;
4286
4288
  if (node.object.type !== AST_NODE_TYPES.ThisExpression || node.computed || node.property.type !== AST_NODE_TYPES.Identifier) return;
4287
4289
  if (node.parent.type === AST_NODE_TYPES.AssignmentExpression && node.parent.left === node) {
4288
- propertyDefs.get(currentClass)?.add(node.property);
4290
+ getOrInsertComputed(propertyDefs, currentClass, () => /* @__PURE__ */ new Set()).add(node.property);
4289
4291
  return;
4290
4292
  }
4291
- propertyUsages.get(currentClass)?.add(node.property.name);
4293
+ getOrInsertComputed(propertyUsages, currentClass, () => /* @__PURE__ */ new Set()).add(node.property.name);
4292
4294
  },
4293
4295
  MethodDefinition: methodEnter,
4294
- "MethodDefinition:exit": methodExit,
4295
4296
  PropertyDefinition: methodEnter,
4296
- "PropertyDefinition:exit": methodExit,
4297
4297
  VariableDeclarator(node) {
4298
- const currentClass = classStack.at(-1);
4299
- const currentMethod = methodStack.at(-1);
4298
+ const currentClass = Traverse.findParent(node, Check.isClass);
4299
+ const currentMethod = Traverse.findParent(node, Check.isPropertyOrMethod);
4300
4300
  if (currentClass == null || currentMethod == null) return;
4301
4301
  if (!core.isClassComponent(currentClass) || currentMethod.static) return;
4302
4302
  if (node.init != null && node.init.type === AST_NODE_TYPES.ThisExpression && node.id.type === AST_NODE_TYPES.ObjectPattern) {
4303
- for (const prop of node.id.properties) if (prop.type === AST_NODE_TYPES.Property && !prop.computed && prop.key.type === AST_NODE_TYPES.Identifier) propertyUsages.get(currentClass)?.add(prop.key.name);
4303
+ for (const prop of node.id.properties) if (prop.type === AST_NODE_TYPES.Property && !prop.computed && prop.key.type === AST_NODE_TYPES.Identifier) getOrInsertComputed(propertyUsages, currentClass, () => /* @__PURE__ */ new Set()).add(prop.key.name);
4304
4304
  }
4305
4305
  }
4306
4306
  };
@@ -4905,6 +4905,23 @@ function isCatalogObject(name) {
4905
4905
  return IMPURE_FUNCS.has(name) || PURE_FUNCS.has(name);
4906
4906
  }
4907
4907
  /**
4908
+ * Check if the program has a top-level `use client` directive, marking the
4909
+ * module as a client module in React Server Components conventions.
4910
+ */
4911
+ function hasUseClientDirective(program) {
4912
+ return program.body.some((stmt) => stmt.type === AST_NODE_TYPES.ExpressionStatement && stmt.directive === "use client");
4913
+ }
4914
+ /**
4915
+ * Check if the node is an argument of a `useRef`-like call, e.g.
4916
+ * `useRef(document.createElement("div"))`. The initializer is evaluated on
4917
+ * every render but stored only on the first one, which is a widely used idiom
4918
+ * (ex: portals), so impure calls in this position are exempted.
4919
+ */
4920
+ function isUseRefInitializerArgument(node) {
4921
+ const { parent } = node;
4922
+ return parent.type === AST_NODE_TYPES.CallExpression && parent.arguments.some((arg) => arg === node) && core.isUseRefLikeCall(parent);
4923
+ }
4924
+ /**
4908
4925
  * Find the property key under which `name` is bound in an object destructuring
4909
4926
  * pattern, e.g. `"random"` for `random` in `const { random } = Math` or for
4910
4927
  * `r` in `const { random: r } = Math`.
@@ -5017,6 +5034,7 @@ function create$7(context) {
5017
5034
  const resolved = resolveBuiltinObjectName(context, expr);
5018
5035
  if (resolved == null) return;
5019
5036
  if (!(resolved.property != null ? IMPURE_FUNCS.get(resolved.object)?.has(resolved.property) ?? false : IMPURE_FUNCS.get("globalThis")?.has(resolved.object) ?? false)) return;
5037
+ if (isUseRefInitializerArgument(node)) return;
5020
5038
  const func = Traverse.findParent(node, Check.isFunction);
5021
5039
  if (func == null) return;
5022
5040
  cEntries.push({
@@ -5029,6 +5047,7 @@ function create$7(context) {
5029
5047
  const resolved = resolveBuiltinMember(context, expr);
5030
5048
  if (resolved == null || resolved.property == null) return;
5031
5049
  if (!IMPURE_FUNCS.get(resolved.object)?.has(resolved.property)) return;
5050
+ if (isUseRefInitializerArgument(node)) return;
5032
5051
  const func = Traverse.findParent(node, Check.isFunction);
5033
5052
  if (func == null) return;
5034
5053
  cEntries.push({
@@ -5047,6 +5066,7 @@ function create$7(context) {
5047
5066
  const ctorName = resolved.property != null && isCatalogObject(resolved.object) ? resolved.property : resolved.object;
5048
5067
  if (!IMPURE_CTORS.has(ctorName)) return;
5049
5068
  if (ctorName === "Date" && node.arguments.length > 0) return;
5069
+ if (isUseRefInitializerArgument(node)) return;
5050
5070
  const func = Traverse.findParent(node, Check.isFunction);
5051
5071
  if (func == null) return;
5052
5072
  nEntries.push({
@@ -5058,8 +5078,10 @@ function create$7(context) {
5058
5078
  const comps = fc.api.getAllComponents(node);
5059
5079
  const hooks = hc.api.getAllHooks(node);
5060
5080
  const funcs = [...comps, ...hooks];
5081
+ const isClientModule = hasUseClientDirective(node);
5061
5082
  for (const { func, node } of [...cEntries, ...nEntries]) {
5062
5083
  if (!funcs.some((f) => f.node === func)) continue;
5084
+ if (!isClientModule && func.async && comps.some((comp) => comp.node === func)) continue;
5063
5085
  context.report({
5064
5086
  data: { name: context.sourceCode.getText(node) },
5065
5087
  messageId: "default",
@@ -5364,9 +5386,7 @@ function createFactCollector() {
5364
5386
  //#region src/rules/refs/effects.ts
5365
5387
  /** The nearest strict ancestor of `node` that is a component or hook. */
5366
5388
  function findBoundary(node, boundaries) {
5367
- return Traverse.findParent(node, (candidate) => {
5368
- return Check.isFunction(candidate) && boundaries.has(candidate);
5369
- });
5389
+ return Traverse.findParent(node, (candidate) => Check.isFunction(candidate) && boundaries.has(candidate));
5370
5390
  }
5371
5391
  /** The boundary containing `node`, where `node` is the nearest enclosing function of the fact. */
5372
5392
  function getBoundaryOf(node, boundaries) {
@@ -7958,10 +7978,14 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
7958
7978
  seen.add(name);
7959
7979
  for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
7960
7980
  if (node.type !== AST_NODE_TYPES.VariableDeclarator) continue;
7961
- const init = node.init;
7981
+ const init = node.init == null ? null : Extract.unwrap(node.init);
7962
7982
  if (init == null) continue;
7963
7983
  switch (true) {
7964
- case init.type === AST_NODE_TYPES.MemberExpression && Check.isIdentifier(init.object) && (init.object.name === "ref" || init.object.name.endsWith("Ref")): return true;
7984
+ case init.type === AST_NODE_TYPES.MemberExpression: {
7985
+ let object = Extract.unwrap(init.object);
7986
+ while (object.type === AST_NODE_TYPES.MemberExpression) object = Extract.unwrap(object.object);
7987
+ return Check.isIdentifier(object) && (object.name === "ref" || object.name.endsWith("Ref"));
7988
+ }
7965
7989
  case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefLikeCall(init, additionalRefHooks): return true;
7966
7990
  case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
7967
7991
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.21.0",
3
+ "version": "5.21.2",
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.21.0",
40
- "@eslint-react/core": "5.21.0",
41
- "@eslint-react/eslint": "5.21.0",
42
- "@eslint-react/jsx": "5.21.0",
43
- "@eslint-react/shared": "5.21.0",
44
- "@eslint-react/var": "5.21.0",
39
+ "@eslint-react/ast": "5.21.2",
40
+ "@eslint-react/core": "5.21.2",
41
+ "@eslint-react/eslint": "5.21.2",
42
+ "@eslint-react/jsx": "5.21.2",
43
+ "@eslint-react/shared": "5.21.2",
44
+ "@eslint-react/var": "5.21.2",
45
45
  "@typescript-eslint/scope-manager": "^8.70.1",
46
46
  "@typescript-eslint/types": "^8.70.1",
47
47
  "@typescript-eslint/typescript-estree": "^8.70.1",