eslint-plugin-react-x 5.17.3 → 5.18.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 +62 -45
- package/package.json +15 -15
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { compare } from "compare-versions";
|
|
|
12
12
|
import { P, isMatching, match } from "ts-pattern";
|
|
13
13
|
import { getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
|
|
14
14
|
import { unionConstituents } from "ts-api-utils";
|
|
15
|
+
import "typescript";
|
|
15
16
|
import { simpleTraverse } from "@typescript-eslint/typescript-estree";
|
|
16
17
|
import { delimiterCase, snakeCase, toLowerCase } from "string-ts";
|
|
17
18
|
|
|
@@ -144,7 +145,7 @@ const rules$6 = {
|
|
|
144
145
|
//#endregion
|
|
145
146
|
//#region package.json
|
|
146
147
|
var name$6 = "eslint-plugin-react-x";
|
|
147
|
-
var version = "5.
|
|
148
|
+
var version = "5.18.0";
|
|
148
149
|
|
|
149
150
|
//#endregion
|
|
150
151
|
//#region src/utils/create-rule.ts
|
|
@@ -1391,7 +1392,8 @@ function isInitializedFromCall(context, node, isCall) {
|
|
|
1391
1392
|
return init.type === AST_NODE_TYPES.CallExpression && isCall(init);
|
|
1392
1393
|
}
|
|
1393
1394
|
function isInitializedFromUseRef(context, node) {
|
|
1394
|
-
|
|
1395
|
+
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
1396
|
+
return isInitializedFromCall(context, node, (init) => core.isUseRefLikeCall(init, additionalRefHooks));
|
|
1395
1397
|
}
|
|
1396
1398
|
function isKnownNonMutatingMethodCall(context, node) {
|
|
1397
1399
|
const callee = Extract.unwrap(node.callee);
|
|
@@ -2377,6 +2379,42 @@ function buildFixForComponentProps(context, fixer, node, typeArguments) {
|
|
|
2377
2379
|
].join(" ")), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])]];
|
|
2378
2380
|
}
|
|
2379
2381
|
|
|
2382
|
+
//#endregion
|
|
2383
|
+
//#region src/utils/create-implicit-prop-listener.ts
|
|
2384
|
+
/**
|
|
2385
|
+
* Creates a listener that detects JSX spread attributes implicitly passing a given prop
|
|
2386
|
+
* (ex: 'children', 'key', 'ref') to a component.
|
|
2387
|
+
*
|
|
2388
|
+
* For each union constituent of the spread argument's type, the prop is reported unless:
|
|
2389
|
+
* 1. The prop symbol's fully qualified name is allowed (pass-through of React internally defined props)
|
|
2390
|
+
* 2. The fully qualified name of the prop's type symbol (or its alias) is allowed (React type aliases)
|
|
2391
|
+
*
|
|
2392
|
+
* @param context The ESLint rule context
|
|
2393
|
+
* @param options The prop name, the allow-list predicates (receive lowercased fully qualified names), and the report callback
|
|
2394
|
+
* @param options.name The name of the prop to detect
|
|
2395
|
+
* @param options.isAllowedProp A predicate that receives the prop symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
2396
|
+
* @param options.isAllowedType A predicate that receives the prop's type symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
2397
|
+
* @param options.onImplicitProp A callback invoked with the spread attribute node when an implicit prop is detected
|
|
2398
|
+
* @returns A rule listener reporting on JSX spread attributes
|
|
2399
|
+
*/
|
|
2400
|
+
function createImplicitPropListener(context, options) {
|
|
2401
|
+
const { name, isAllowedProp, isAllowedType, onImplicitProp } = options;
|
|
2402
|
+
const services = ESLintUtils.getParserServices(context, false);
|
|
2403
|
+
const checker = services.program.getTypeChecker();
|
|
2404
|
+
const getFqn = (symbol) => core.getFullyQualifiedNameEx(checker, symbol).toLowerCase();
|
|
2405
|
+
return { JSXSpreadAttribute(node) {
|
|
2406
|
+
for (const type of unionConstituents(getConstrainedTypeAtLocation(services, node.argument))) {
|
|
2407
|
+
const prop = type.getProperty(name);
|
|
2408
|
+
if (prop == null) continue;
|
|
2409
|
+
if (isAllowedProp(getFqn(prop))) continue;
|
|
2410
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
2411
|
+
const propTypeSymbol = propType.aliasSymbol ?? propType.symbol;
|
|
2412
|
+
if (propTypeSymbol != null && isAllowedType(getFqn(propTypeSymbol))) continue;
|
|
2413
|
+
onImplicitProp(node);
|
|
2414
|
+
}
|
|
2415
|
+
} };
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2380
2418
|
//#endregion
|
|
2381
2419
|
//#region src/rules/no-implicit-children/no-implicit-children.ts
|
|
2382
2420
|
const RULE_NAME$30 = "no-implicit-children";
|
|
@@ -2393,26 +2431,17 @@ var no_implicit_children_default = createRule({
|
|
|
2393
2431
|
defaultOptions: []
|
|
2394
2432
|
});
|
|
2395
2433
|
function create$30(context) {
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
if (children == null) continue;
|
|
2402
|
-
const fqn = core.getFullyQualifiedNameEx(checker, children).toLowerCase();
|
|
2403
|
-
if (fqn.endsWith("attributes.children") || fqn.endsWith("propswithchildren.children")) continue;
|
|
2404
|
-
const childrenType = checker.getTypeOfSymbol(children);
|
|
2405
|
-
const typeSymbol = childrenType.aliasSymbol ?? childrenType.symbol;
|
|
2406
|
-
if (typeSymbol != null) {
|
|
2407
|
-
const typeFqn = checker.getFullyQualifiedName(typeSymbol);
|
|
2408
|
-
if (RE_REACT_CHILDREN_TYPE.test(typeFqn) || /^JSX\.Element$/i.test(typeFqn)) continue;
|
|
2409
|
-
}
|
|
2434
|
+
return createImplicitPropListener(context, {
|
|
2435
|
+
name: "children",
|
|
2436
|
+
isAllowedProp: (fqn) => fqn.endsWith("attributes.children") || fqn.endsWith("propswithchildren.children"),
|
|
2437
|
+
isAllowedType: (fqn) => RE_REACT_CHILDREN_TYPE.test(fqn) || fqn === "jsx.element",
|
|
2438
|
+
onImplicitProp(node) {
|
|
2410
2439
|
context.report({
|
|
2411
2440
|
messageId: "default",
|
|
2412
2441
|
node
|
|
2413
2442
|
});
|
|
2414
2443
|
}
|
|
2415
|
-
}
|
|
2444
|
+
});
|
|
2416
2445
|
}
|
|
2417
2446
|
|
|
2418
2447
|
//#endregion
|
|
@@ -2430,23 +2459,17 @@ var no_implicit_key_default = createRule({
|
|
|
2430
2459
|
defaultOptions: []
|
|
2431
2460
|
});
|
|
2432
2461
|
function create$29(context) {
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
if (key == null) continue;
|
|
2439
|
-
if (core.getFullyQualifiedNameEx(checker, key).toLowerCase().endsWith("react.attributes.key")) continue;
|
|
2440
|
-
const keyType = checker.getTypeOfSymbol(key);
|
|
2441
|
-
if (keyType.aliasSymbol != null) {
|
|
2442
|
-
if (checker.getFullyQualifiedName(keyType.aliasSymbol).toLowerCase().endsWith("react.key")) continue;
|
|
2443
|
-
}
|
|
2462
|
+
return createImplicitPropListener(context, {
|
|
2463
|
+
name: "key",
|
|
2464
|
+
isAllowedProp: (fqn) => fqn.endsWith("react.attributes.key"),
|
|
2465
|
+
isAllowedType: (fqn) => fqn.endsWith("react.key"),
|
|
2466
|
+
onImplicitProp(node) {
|
|
2444
2467
|
context.report({
|
|
2445
2468
|
messageId: "default",
|
|
2446
2469
|
node
|
|
2447
2470
|
});
|
|
2448
2471
|
}
|
|
2449
|
-
}
|
|
2472
|
+
});
|
|
2450
2473
|
}
|
|
2451
2474
|
|
|
2452
2475
|
//#endregion
|
|
@@ -2465,25 +2488,17 @@ var no_implicit_ref_default = createRule({
|
|
|
2465
2488
|
defaultOptions: []
|
|
2466
2489
|
});
|
|
2467
2490
|
function create$28(context) {
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
if (ref == null) continue;
|
|
2474
|
-
if (core.getFullyQualifiedNameEx(checker, ref).toLowerCase().endsWith("attributes.ref")) continue;
|
|
2475
|
-
const refType = checker.getTypeOfSymbol(ref);
|
|
2476
|
-
const typeSymbol = refType.aliasSymbol ?? refType.symbol;
|
|
2477
|
-
if (typeSymbol != null) {
|
|
2478
|
-
const typeFqn = checker.getFullyQualifiedName(typeSymbol);
|
|
2479
|
-
if (RE_REACT_REF_TYPE.test(typeFqn)) continue;
|
|
2480
|
-
}
|
|
2491
|
+
return createImplicitPropListener(context, {
|
|
2492
|
+
name: "ref",
|
|
2493
|
+
isAllowedProp: (fqn) => fqn.endsWith("attributes.ref"),
|
|
2494
|
+
isAllowedType: (fqn) => RE_REACT_REF_TYPE.test(fqn),
|
|
2495
|
+
onImplicitProp(node) {
|
|
2481
2496
|
context.report({
|
|
2482
2497
|
messageId: "default",
|
|
2483
2498
|
node
|
|
2484
2499
|
});
|
|
2485
2500
|
}
|
|
2486
|
-
}
|
|
2501
|
+
});
|
|
2487
2502
|
}
|
|
2488
2503
|
|
|
2489
2504
|
//#endregion
|
|
@@ -4682,6 +4697,7 @@ const SYNC_ARRAY_CALLBACKS = /* @__PURE__ */ new Set([
|
|
|
4682
4697
|
"sort"
|
|
4683
4698
|
]);
|
|
4684
4699
|
function createBindingResolver(context) {
|
|
4700
|
+
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
4685
4701
|
const bindings = /* @__PURE__ */ new Map();
|
|
4686
4702
|
const memberBindings = /* @__PURE__ */ new Map();
|
|
4687
4703
|
const jsxRefs = /* @__PURE__ */ new Set();
|
|
@@ -4701,7 +4717,7 @@ function createBindingResolver(context) {
|
|
|
4701
4717
|
kind: "function",
|
|
4702
4718
|
node: value
|
|
4703
4719
|
};
|
|
4704
|
-
if (value.type === AST_NODE_TYPES.CallExpression && (core.
|
|
4720
|
+
if (value.type === AST_NODE_TYPES.CallExpression && (core.isUseRefLikeCall(value, additionalRefHooks) || core.isCreateRefCall(context, value))) return { kind: "ref" };
|
|
4705
4721
|
if (value.type === AST_NODE_TYPES.MemberExpression && value.property.type === AST_NODE_TYPES.Identifier) {
|
|
4706
4722
|
if (isRefLikeName(value.property.name)) return { kind: "ref" };
|
|
4707
4723
|
}
|
|
@@ -7344,6 +7360,7 @@ function isHookDecl(node) {
|
|
|
7344
7360
|
return name != null && core.isHookName(name);
|
|
7345
7361
|
}
|
|
7346
7362
|
function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ */ new Set()) {
|
|
7363
|
+
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
7347
7364
|
if (seen.has(name)) return false;
|
|
7348
7365
|
seen.add(name);
|
|
7349
7366
|
for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
|
|
@@ -7352,7 +7369,7 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
|
|
|
7352
7369
|
if (init == null) continue;
|
|
7353
7370
|
switch (true) {
|
|
7354
7371
|
case init.type === AST_NODE_TYPES.MemberExpression && init.object.type === AST_NODE_TYPES.Identifier && (init.object.name === "ref" || init.object.name.endsWith("Ref")): return true;
|
|
7355
|
-
case init.type === AST_NODE_TYPES.CallExpression && core.
|
|
7372
|
+
case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefLikeCall(init, additionalRefHooks): return true;
|
|
7356
7373
|
case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
|
|
7357
7374
|
}
|
|
7358
7375
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.18.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",
|
|
@@ -36,30 +36,30 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@typescript-eslint/scope-manager": "^8.
|
|
40
|
-
"@typescript-eslint/type-utils": "^8.
|
|
41
|
-
"@typescript-eslint/types": "^8.
|
|
42
|
-
"@typescript-eslint/typescript-estree": "^8.
|
|
43
|
-
"@typescript-eslint/utils": "^8.
|
|
39
|
+
"@typescript-eslint/scope-manager": "^8.65.0",
|
|
40
|
+
"@typescript-eslint/type-utils": "^8.65.0",
|
|
41
|
+
"@typescript-eslint/types": "^8.65.0",
|
|
42
|
+
"@typescript-eslint/typescript-estree": "^8.65.0",
|
|
43
|
+
"@typescript-eslint/utils": "^8.65.0",
|
|
44
44
|
"compare-versions": "^6.1.1",
|
|
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": "5.
|
|
49
|
-
"@eslint-react/eslint": "5.
|
|
50
|
-
"@eslint-react/
|
|
51
|
-
"@eslint-react/
|
|
52
|
-
"@eslint-react/shared": "5.
|
|
53
|
-
"@eslint-react/var": "5.
|
|
48
|
+
"@eslint-react/ast": "5.18.0",
|
|
49
|
+
"@eslint-react/eslint": "5.18.0",
|
|
50
|
+
"@eslint-react/core": "5.18.0",
|
|
51
|
+
"@eslint-react/jsx": "5.18.0",
|
|
52
|
+
"@eslint-react/shared": "5.18.0",
|
|
53
|
+
"@eslint-react/var": "5.18.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/react": "^19.2.17",
|
|
57
57
|
"@types/react-dom": "^19.2.3",
|
|
58
58
|
"dedent": "^1.7.2",
|
|
59
59
|
"eslint": "^10.7.0",
|
|
60
|
-
"react": "^19.2.
|
|
61
|
-
"react-dom": "^19.2.
|
|
62
|
-
"tsdown": "^0.22.
|
|
60
|
+
"react": "^19.2.8",
|
|
61
|
+
"react-dom": "^19.2.8",
|
|
62
|
+
"tsdown": "^0.22.13",
|
|
63
63
|
"tsl": "^1.0.30",
|
|
64
64
|
"tsl-dx": "^0.13.3",
|
|
65
65
|
"typescript": "6.0.3",
|