eslint-plugin-react-x 5.28.1 → 5.28.3

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 +32 -36
  2. package/package.json +11 -11
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { P, isMatching, match } from "ts-pattern";
8
8
  import { isAssignmentTargetEqual, isInitializedFromReact, resolveEnclosingAssignmentTarget, resolveObjectType, resolveOrigin } from "@eslint-react/var";
9
9
  import { DefinitionType, ScopeType } from "@typescript-eslint/scope-manager";
10
10
  import { findVariable, getStaticValue } from "@typescript-eslint/utils/ast-utils";
11
- import { findParentAttribute, getElementFullType, hasAttribute } from "@eslint-react/jsx";
11
+ import { findParentAttribute, getElementFullType, hasAttribute, isAttribute } from "@eslint-react/jsx";
12
12
  import { compare } from "compare-versions";
13
13
  import { unionConstituents } from "ts-api-utils";
14
14
  import "typescript";
@@ -135,7 +135,7 @@ const rules$6 = {
135
135
  //#endregion
136
136
  //#region package.json
137
137
  var name$6 = "eslint-plugin-react-x";
138
- var version = "5.28.1";
138
+ var version = "5.28.3";
139
139
  //#endregion
140
140
  //#region src/utils/create-rule.ts
141
141
  function getDocsUrl(ruleName) {
@@ -3232,40 +3232,36 @@ function create$13(context) {
3232
3232
  if (compilationMode === "annotation" && context.sourceCode.ast.body.some((stmt) => Check.isDirective(stmt, "use memo"))) return {};
3233
3233
  const isReact18OrBelow = compare(version, "19.0.0", "<");
3234
3234
  const { api, visitor } = core.getFunctionComponentCollector(context);
3235
- const constructions = /* @__PURE__ */ new WeakMap();
3235
+ const valueExpressions = [];
3236
3236
  return merge(visitor, {
3237
3237
  JSXOpeningElement(node) {
3238
3238
  const selfName = getElementFullType(node.parent).split(".").at(-1);
3239
3239
  if (selfName == null) return;
3240
3240
  if (!isContextName(selfName, isReact18OrBelow)) return;
3241
- const enclosingFunction = Traverse.findParent(node, Check.isFunction);
3242
- if (enclosingFunction == null) return;
3243
- if (compilationMode === "annotation" && core.isFunctionHasDirective(enclosingFunction, "use memo")) return;
3244
- const attribute = node.attributes.find((attribute) => attribute.type === AST_NODE_TYPES.JSXAttribute && attribute.name.name === "value");
3245
- if (attribute == null || !hasProperty(attribute, "value")) return;
3246
- const value = attribute.value;
3247
- if (value?.type !== AST_NODE_TYPES.JSXExpressionContainer) return;
3248
- const valueExpression = value.expression;
3249
- const construction = resolveObjectType(context, valueExpression);
3250
- if (construction == null) return;
3251
- if (core.isHookCall(construction.node)) return;
3252
- getOrInsertComputed(constructions, enclosingFunction, () => []).push(construction);
3241
+ const attribute = node.attributes.findLast(isAttribute("value"));
3242
+ if (attribute?.value?.type !== AST_NODE_TYPES.JSXExpressionContainer) return;
3243
+ const { expression } = attribute.value;
3244
+ if (expression.type === AST_NODE_TYPES.JSXEmptyExpression) return;
3245
+ valueExpressions.push(expression);
3253
3246
  },
3254
3247
  "Program:exit"(program) {
3255
- for (const { directives, node: component } of api.getAllComponents(program)) {
3256
- if (compilationMode === "annotation" && directives.some((d) => d.directive === "use memo")) continue;
3257
- for (const construction of constructions.get(component) ?? []) {
3258
- const { kind, node: constructionNode } = construction;
3259
- const suggestion = kind === "function" ? "Consider wrapping it in a useCallback hook." : "Consider wrapping it in a useMemo hook.";
3260
- context.report({
3261
- data: {
3262
- kind: getHumanReadableKind$1(constructionNode),
3263
- suggestion
3264
- },
3265
- messageId: "unstable-context-value",
3266
- node: constructionNode
3267
- });
3268
- }
3248
+ const components = api.getAllComponents(program).filter(({ directives }) => compilationMode !== "annotation" || !directives.some((d) => d.directive === "use memo")).map(({ node }) => node).sort((a, b) => a.range[1] - a.range[0] - (b.range[1] - b.range[0]));
3249
+ for (const valueExpression of valueExpressions) {
3250
+ const component = components.find((node) => node.range[0] <= valueExpression.range[0] && valueExpression.range[1] <= node.range[1]);
3251
+ if (component == null) continue;
3252
+ const construction = resolveObjectType(context, valueExpression, { boundary: component });
3253
+ if (construction == null) continue;
3254
+ if (core.isHookCall(construction.node)) continue;
3255
+ const { kind, node: constructionNode } = construction;
3256
+ const suggestion = kind === "function" ? "Consider wrapping it in a useCallback hook." : "Consider wrapping it in a useMemo hook.";
3257
+ context.report({
3258
+ data: {
3259
+ kind: getHumanReadableKind$1(constructionNode),
3260
+ suggestion
3261
+ },
3262
+ messageId: "unstable-context-value",
3263
+ node: constructionNode
3264
+ });
3269
3265
  }
3270
3266
  }
3271
3267
  });
@@ -3313,28 +3309,28 @@ function create$12(context, [options]) {
3313
3309
  if (compilationMode === "infer" || compilationMode === "all") return {};
3314
3310
  if (compilationMode === "annotation" && context.sourceCode.ast.body.some((stmt) => Check.isDirective(stmt, "use memo"))) return {};
3315
3311
  const { api, visitor } = core.getFunctionComponentCollector(context);
3316
- const declarators = /* @__PURE__ */ new WeakMap();
3312
+ const declarators = [];
3317
3313
  const { safeDefaultProps = [] } = options;
3318
3314
  const safePatterns = safeDefaultProps.map((s) => toRegExp(s));
3319
3315
  return merge(visitor, {
3320
3316
  [SEL_OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR](node) {
3321
- const enclosingFunction = Traverse.findParent(node, Check.isFunction);
3322
- if (enclosingFunction == null) return;
3323
- getOrInsertComputed(declarators, enclosingFunction, () => []).push(node);
3317
+ declarators.push(node);
3324
3318
  },
3325
3319
  "Program:exit"(program) {
3326
- for (const { node: component } of api.getAllComponents(program)) {
3320
+ const components = api.getAllComponents(program).map(({ node }) => node).sort((a, b) => a.range[1] - a.range[0] - (b.range[1] - b.range[0]));
3321
+ const innermostComponentOf = (node) => components.find((component) => component.range[0] <= node.range[0] && node.range[1] <= component.range[1]);
3322
+ for (const component of components) {
3327
3323
  const { params } = component;
3328
3324
  const [props] = params;
3329
3325
  if (props == null) continue;
3330
3326
  const properties = match(props).with({ type: AST_NODE_TYPES.ObjectPattern }, ({ properties }) => properties).with({ type: AST_NODE_TYPES.Identifier }, ({ name }) => {
3331
- return declarators.get(component)?.filter((d) => d.init.name === name).flatMap((d) => d.id.properties) ?? [];
3327
+ return declarators.filter((d) => d.init.name === name && innermostComponentOf(d) === component).flatMap((d) => d.id.properties);
3332
3328
  }).otherwise(() => []);
3333
3329
  for (const prop of properties) {
3334
3330
  if (prop.type !== AST_NODE_TYPES.Property || prop.value.type !== AST_NODE_TYPES.AssignmentPattern) continue;
3335
3331
  const { value } = prop;
3336
3332
  const { right } = value;
3337
- const construction = resolveObjectType(context, value);
3333
+ const construction = resolveObjectType(context, value, { boundary: component });
3338
3334
  if (construction == null) continue;
3339
3335
  if (core.isHookCall(construction.node)) continue;
3340
3336
  if (safePatterns.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "5.28.1",
3
+ "version": "5.28.3",
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,16 +36,16 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@eslint-react/ast": "5.28.1",
40
- "@eslint-react/core": "5.28.1",
41
- "@eslint-react/eslint": "5.28.1",
42
- "@eslint-react/jsx": "5.28.1",
43
- "@eslint-react/shared": "5.28.1",
44
- "@eslint-react/var": "5.28.1",
45
- "@typescript-eslint/scope-manager": "^8.71.2",
46
- "@typescript-eslint/types": "^8.71.2",
47
- "@typescript-eslint/typescript-estree": "^8.71.2",
48
- "@typescript-eslint/utils": "^8.71.2",
39
+ "@eslint-react/ast": "5.28.3",
40
+ "@eslint-react/core": "5.28.3",
41
+ "@eslint-react/eslint": "5.28.3",
42
+ "@eslint-react/jsx": "5.28.3",
43
+ "@eslint-react/shared": "5.28.3",
44
+ "@eslint-react/var": "5.28.3",
45
+ "@typescript-eslint/scope-manager": "^8.72.0",
46
+ "@typescript-eslint/types": "^8.72.0",
47
+ "@typescript-eslint/typescript-estree": "^8.72.0",
48
+ "@typescript-eslint/utils": "^8.72.0",
49
49
  "compare-versions": "^6.1.1",
50
50
  "string-ts": "^2.3.1",
51
51
  "ts-api-utils": "^2.5.0",