eslint-plugin-react-x 5.21.1 → 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 +61 -20
  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.1";
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)), Check.isClass);
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
@@ -4244,6 +4260,7 @@ function create$11(context) {
4244
4260
  if (LIFECYCLE_METHODS.has(methodName)) {
4245
4261
  if (methodName === "shouldComponentUpdate" && core.isPureComponent(node)) {} else continue;
4246
4262
  }
4263
+ if (HOST_CONVENTION_METHODS.has(methodName)) continue;
4247
4264
  context.report({
4248
4265
  data: {
4249
4266
  className: id != null ? context.sourceCode.getText(id) : "Component",
@@ -4888,6 +4905,23 @@ function isCatalogObject(name) {
4888
4905
  return IMPURE_FUNCS.has(name) || PURE_FUNCS.has(name);
4889
4906
  }
4890
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
+ /**
4891
4925
  * Find the property key under which `name` is bound in an object destructuring
4892
4926
  * pattern, e.g. `"random"` for `random` in `const { random } = Math` or for
4893
4927
  * `r` in `const { random: r } = Math`.
@@ -5000,6 +5034,7 @@ function create$7(context) {
5000
5034
  const resolved = resolveBuiltinObjectName(context, expr);
5001
5035
  if (resolved == null) return;
5002
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;
5003
5038
  const func = Traverse.findParent(node, Check.isFunction);
5004
5039
  if (func == null) return;
5005
5040
  cEntries.push({
@@ -5012,6 +5047,7 @@ function create$7(context) {
5012
5047
  const resolved = resolveBuiltinMember(context, expr);
5013
5048
  if (resolved == null || resolved.property == null) return;
5014
5049
  if (!IMPURE_FUNCS.get(resolved.object)?.has(resolved.property)) return;
5050
+ if (isUseRefInitializerArgument(node)) return;
5015
5051
  const func = Traverse.findParent(node, Check.isFunction);
5016
5052
  if (func == null) return;
5017
5053
  cEntries.push({
@@ -5030,6 +5066,7 @@ function create$7(context) {
5030
5066
  const ctorName = resolved.property != null && isCatalogObject(resolved.object) ? resolved.property : resolved.object;
5031
5067
  if (!IMPURE_CTORS.has(ctorName)) return;
5032
5068
  if (ctorName === "Date" && node.arguments.length > 0) return;
5069
+ if (isUseRefInitializerArgument(node)) return;
5033
5070
  const func = Traverse.findParent(node, Check.isFunction);
5034
5071
  if (func == null) return;
5035
5072
  nEntries.push({
@@ -5041,8 +5078,10 @@ function create$7(context) {
5041
5078
  const comps = fc.api.getAllComponents(node);
5042
5079
  const hooks = hc.api.getAllHooks(node);
5043
5080
  const funcs = [...comps, ...hooks];
5081
+ const isClientModule = hasUseClientDirective(node);
5044
5082
  for (const { func, node } of [...cEntries, ...nEntries]) {
5045
5083
  if (!funcs.some((f) => f.node === func)) continue;
5084
+ if (!isClientModule && func.async && comps.some((comp) => comp.node === func)) continue;
5046
5085
  context.report({
5047
5086
  data: { name: context.sourceCode.getText(node) },
5048
5087
  messageId: "default",
@@ -5347,9 +5386,7 @@ function createFactCollector() {
5347
5386
  //#region src/rules/refs/effects.ts
5348
5387
  /** The nearest strict ancestor of `node` that is a component or hook. */
5349
5388
  function findBoundary(node, boundaries) {
5350
- return Traverse.findParent(node, (candidate) => {
5351
- return Check.isFunction(candidate) && boundaries.has(candidate);
5352
- });
5389
+ return Traverse.findParent(node, (candidate) => Check.isFunction(candidate) && boundaries.has(candidate));
5353
5390
  }
5354
5391
  /** The boundary containing `node`, where `node` is the nearest enclosing function of the fact. */
5355
5392
  function getBoundaryOf(node, boundaries) {
@@ -7941,10 +7978,14 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
7941
7978
  seen.add(name);
7942
7979
  for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
7943
7980
  if (node.type !== AST_NODE_TYPES.VariableDeclarator) continue;
7944
- const init = node.init;
7981
+ const init = node.init == null ? null : Extract.unwrap(node.init);
7945
7982
  if (init == null) continue;
7946
7983
  switch (true) {
7947
- 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
+ }
7948
7989
  case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefLikeCall(init, additionalRefHooks): return true;
7949
7990
  case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
7950
7991
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.21.1",
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.1",
40
- "@eslint-react/core": "5.21.1",
41
- "@eslint-react/eslint": "5.21.1",
42
- "@eslint-react/jsx": "5.21.1",
43
- "@eslint-react/shared": "5.21.1",
44
- "@eslint-react/var": "5.21.1",
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",