eslint-plugin-react-x 4.0.2-beta.6 → 4.1.0-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.
- package/dist/index.js +56 -4
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import { DEFAULT_ESLINT_REACT_SETTINGS, IMPURE_CTORS, IMPURE_FUNCS, WEBSITE_URL,
|
|
|
2
2
|
import * as ast from "@eslint-react/ast";
|
|
3
3
|
import * as core from "@eslint-react/core";
|
|
4
4
|
import { isUseRefCall } from "@eslint-react/core";
|
|
5
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
5
6
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
6
7
|
import { P, isMatching, match } from "ts-pattern";
|
|
7
8
|
import ts from "typescript";
|
|
8
9
|
import { JsxDetectionHint, findParentAttribute, getElementFullType, hasAttribute, isJsxLike } from "@eslint-react/jsx";
|
|
9
|
-
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
10
10
|
import { computeObjectType, findEnclosingAssignmentTarget, isAssignmentTargetEqual, resolve } from "@eslint-react/var";
|
|
11
11
|
import { DefinitionType } from "@typescript-eslint/scope-manager";
|
|
12
12
|
import { findVariable, getStaticValue, isIdentifier, isVariableDeclarator } from "@typescript-eslint/utils/ast-utils";
|
|
@@ -147,7 +147,7 @@ const rules$7 = {
|
|
|
147
147
|
//#endregion
|
|
148
148
|
//#region package.json
|
|
149
149
|
var name$6 = "eslint-plugin-react-x";
|
|
150
|
-
var version = "4.0
|
|
150
|
+
var version = "4.1.0-beta.0";
|
|
151
151
|
|
|
152
152
|
//#endregion
|
|
153
153
|
//#region src/utils/create-rule.ts
|
|
@@ -319,6 +319,51 @@ var component_hook_factories_default = createRule({
|
|
|
319
319
|
create: create$56,
|
|
320
320
|
defaultOptions: []
|
|
321
321
|
});
|
|
322
|
+
/**
|
|
323
|
+
* Check if a function parameter name looks like a React component (PascalCase).
|
|
324
|
+
*/
|
|
325
|
+
function isComponentLikeParamName(name) {
|
|
326
|
+
return /^[A-Z]/.test(name);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Check if a function parameter has a type annotation that looks like a React component type.
|
|
330
|
+
* Matches types like ComponentType, React.ComponentType, FC, React.FC, etc.
|
|
331
|
+
*/
|
|
332
|
+
function hasComponentTypeAnnotation(param) {
|
|
333
|
+
if (param.type !== AST_NODE_TYPES.Identifier || param.typeAnnotation == null) return false;
|
|
334
|
+
const annotation = param.typeAnnotation.typeAnnotation;
|
|
335
|
+
if (annotation.type === AST_NODE_TYPES.TSTypeReference) return isComponentTypeName(annotation.typeName);
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Check if a type name refers to a known React component type.
|
|
340
|
+
*/
|
|
341
|
+
function isComponentTypeName(typeName) {
|
|
342
|
+
if (typeName.type === AST_NODE_TYPES.Identifier) return /^(ComponentType|FC|ComponentClass|FunctionComponent|Component)$/.test(typeName.name);
|
|
343
|
+
if (typeName.type === AST_NODE_TYPES.TSQualifiedName) {
|
|
344
|
+
if (typeName.left.type === AST_NODE_TYPES.Identifier && typeName.left.name === "React") return isComponentTypeName(typeName.right);
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Heuristically check if a function is a Higher Order Component (HOC) based on its parameters.
|
|
350
|
+
* Considers a function an HOC if it takes a parameter that looks like a React component
|
|
351
|
+
* (by name or type annotation). This does not validate that the function actually returns
|
|
352
|
+
* a React component.
|
|
353
|
+
*/
|
|
354
|
+
function isHigherOrderComponent(fn) {
|
|
355
|
+
return fn.params.some((param) => {
|
|
356
|
+
if (param.type === AST_NODE_TYPES.Identifier && isComponentLikeParamName(param.name)) return true;
|
|
357
|
+
if (hasComponentTypeAnnotation(param)) return true;
|
|
358
|
+
return false;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Check if a node is inside a test mock callback (vi.mock or jest.mock).
|
|
363
|
+
*/
|
|
364
|
+
function isInsideTestMockCallback(node) {
|
|
365
|
+
return ast.findParent(node, ast.isTestMockCallback) != null;
|
|
366
|
+
}
|
|
322
367
|
function create$56(context) {
|
|
323
368
|
const hint = core.ComponentDetectionHint.DoNotIncludeJsxWithNumberValue | core.ComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | core.ComponentDetectionHint.DoNotIncludeJsxWithNullValue | core.ComponentDetectionHint.DoNotIncludeJsxWithStringValue | core.ComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | core.ComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx | core.ComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | core.ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayPatternElement | core.ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayExpressionElement | core.ComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback;
|
|
324
369
|
const fCollector = core.getComponentCollector(context, { hint });
|
|
@@ -331,7 +376,10 @@ function create$56(context) {
|
|
|
331
376
|
const hooks = [...hCollector.api.getAllHooks(program)];
|
|
332
377
|
for (const { name, node } of fComponents) {
|
|
333
378
|
if (name == null) continue;
|
|
334
|
-
|
|
379
|
+
const parentFn = ast.findParent(node, ast.isFunction);
|
|
380
|
+
if (parentFn == null) continue;
|
|
381
|
+
if (isInsideTestMockCallback(node)) continue;
|
|
382
|
+
if (isHigherOrderComponent(parentFn)) continue;
|
|
335
383
|
if (reported.has(node)) continue;
|
|
336
384
|
context.report({
|
|
337
385
|
data: { name },
|
|
@@ -341,7 +389,10 @@ function create$56(context) {
|
|
|
341
389
|
reported.add(node);
|
|
342
390
|
}
|
|
343
391
|
for (const { name = "unknown", node } of cComponents) {
|
|
344
|
-
|
|
392
|
+
const parentFn = ast.findParent(node, ast.isFunction);
|
|
393
|
+
if (parentFn == null) continue;
|
|
394
|
+
if (isInsideTestMockCallback(node)) continue;
|
|
395
|
+
if (isHigherOrderComponent(parentFn)) continue;
|
|
345
396
|
context.report({
|
|
346
397
|
data: { name },
|
|
347
398
|
messageId: "component",
|
|
@@ -350,6 +401,7 @@ function create$56(context) {
|
|
|
350
401
|
}
|
|
351
402
|
for (const { name, node } of hooks) {
|
|
352
403
|
if (ast.findParent(node, ast.isFunction) == null) continue;
|
|
404
|
+
if (isInsideTestMockCallback(node)) continue;
|
|
353
405
|
if (reported.has(node)) continue;
|
|
354
406
|
context.report({
|
|
355
407
|
data: { name },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0-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",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"string-ts": "^2.3.1",
|
|
46
46
|
"ts-api-utils": "^2.5.0",
|
|
47
47
|
"ts-pattern": "^5.9.0",
|
|
48
|
-
"@eslint-react/ast": "4.0
|
|
49
|
-
"@eslint-react/core": "4.0
|
|
50
|
-
"@eslint-react/shared": "4.0
|
|
51
|
-
"@eslint-react/jsx": "4.0
|
|
52
|
-
"@eslint-react/var": "4.0
|
|
48
|
+
"@eslint-react/ast": "4.1.0-beta.0",
|
|
49
|
+
"@eslint-react/core": "4.1.0-beta.0",
|
|
50
|
+
"@eslint-react/shared": "4.1.0-beta.0",
|
|
51
|
+
"@eslint-react/jsx": "4.1.0-beta.0",
|
|
52
|
+
"@eslint-react/var": "4.1.0-beta.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/react": "^19.2.14",
|