eslint-plugin-react-x 5.17.3 → 5.18.1
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 +64 -68
- package/package.json +16 -16
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.1";
|
|
148
149
|
|
|
149
150
|
//#endregion
|
|
150
151
|
//#region src/utils/create-rule.ts
|
|
@@ -170,27 +171,6 @@ var error_boundaries_default = createRule({
|
|
|
170
171
|
create: create$50,
|
|
171
172
|
defaultOptions: []
|
|
172
173
|
});
|
|
173
|
-
/**
|
|
174
|
-
* Finds the nearest TryStatement whose `try` block (not catch/finally)
|
|
175
|
-
* encloses the given node.
|
|
176
|
-
* @param node The node to check.
|
|
177
|
-
* @returns The enclosing TryStatement, or null if none is found.
|
|
178
|
-
*/
|
|
179
|
-
function getEnclosingTryBlock(node) {
|
|
180
|
-
let current = node.parent;
|
|
181
|
-
while (current != null) {
|
|
182
|
-
if (current.type === AST_NODE_TYPES.TryStatement) {
|
|
183
|
-
let n = node;
|
|
184
|
-
while (n != null && n !== current) {
|
|
185
|
-
if (n === current.block) return current;
|
|
186
|
-
n = n.parent;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (current.type === AST_NODE_TYPES.Program) return null;
|
|
190
|
-
current = current.parent;
|
|
191
|
-
}
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
174
|
function create$50(context) {
|
|
195
175
|
if (!context.sourceCode.text.includes("try")) return {};
|
|
196
176
|
const hint = core.JsxDetectionHint.DoNotIncludeJsxWithNullValue | core.JsxDetectionHint.DoNotIncludeJsxWithNumberValue | core.JsxDetectionHint.DoNotIncludeJsxWithBigIntValue | core.JsxDetectionHint.DoNotIncludeJsxWithStringValue | core.JsxDetectionHint.DoNotIncludeJsxWithBooleanValue | core.JsxDetectionHint.DoNotIncludeJsxWithUndefinedValue | core.JsxDetectionHint.DoNotIncludeJsxWithEmptyArrayValue;
|
|
@@ -208,7 +188,7 @@ function create$50(context) {
|
|
|
208
188
|
const hooks = hc.api.getAllHooks(node);
|
|
209
189
|
const funcs = [...comps, ...hooks];
|
|
210
190
|
for (const call of useCalls) {
|
|
211
|
-
const stmt =
|
|
191
|
+
const stmt = Traverse.findEnclosingTryBlock(call);
|
|
212
192
|
const func = Traverse.findParent(stmt, (n) => funcs.some((f) => f.node === n));
|
|
213
193
|
if (stmt != null && func != null && !reported.has(stmt)) {
|
|
214
194
|
context.report({
|
|
@@ -221,7 +201,7 @@ function create$50(context) {
|
|
|
221
201
|
for (const { rets } of funcs) for (const ret of rets) {
|
|
222
202
|
if (ret == null) continue;
|
|
223
203
|
if (!core.isJsxLike(context, ret, hint)) continue;
|
|
224
|
-
const stmt =
|
|
204
|
+
const stmt = Traverse.findEnclosingTryBlock(ret);
|
|
225
205
|
if (stmt != null && !reported.has(stmt)) {
|
|
226
206
|
context.report({
|
|
227
207
|
messageId: "tryCatchWithJsx",
|
|
@@ -1391,7 +1371,8 @@ function isInitializedFromCall(context, node, isCall) {
|
|
|
1391
1371
|
return init.type === AST_NODE_TYPES.CallExpression && isCall(init);
|
|
1392
1372
|
}
|
|
1393
1373
|
function isInitializedFromUseRef(context, node) {
|
|
1394
|
-
|
|
1374
|
+
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
1375
|
+
return isInitializedFromCall(context, node, (init) => core.isUseRefLikeCall(init, additionalRefHooks));
|
|
1395
1376
|
}
|
|
1396
1377
|
function isKnownNonMutatingMethodCall(context, node) {
|
|
1397
1378
|
const callee = Extract.unwrap(node.callee);
|
|
@@ -2377,6 +2358,42 @@ function buildFixForComponentProps(context, fixer, node, typeArguments) {
|
|
|
2377
2358
|
].join(" ")), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])]];
|
|
2378
2359
|
}
|
|
2379
2360
|
|
|
2361
|
+
//#endregion
|
|
2362
|
+
//#region src/utils/create-implicit-prop-listener.ts
|
|
2363
|
+
/**
|
|
2364
|
+
* Creates a listener that detects JSX spread attributes implicitly passing a given prop
|
|
2365
|
+
* (ex: 'children', 'key', 'ref') to a component.
|
|
2366
|
+
*
|
|
2367
|
+
* For each union constituent of the spread argument's type, the prop is reported unless:
|
|
2368
|
+
* 1. The prop symbol's fully qualified name is allowed (pass-through of React internally defined props)
|
|
2369
|
+
* 2. The fully qualified name of the prop's type symbol (or its alias) is allowed (React type aliases)
|
|
2370
|
+
*
|
|
2371
|
+
* @param context The ESLint rule context
|
|
2372
|
+
* @param options The prop name, the allow-list predicates (receive lowercased fully qualified names), and the report callback
|
|
2373
|
+
* @param options.name The name of the prop to detect
|
|
2374
|
+
* @param options.isAllowedProp A predicate that receives the prop symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
2375
|
+
* @param options.isAllowedType A predicate that receives the prop's type symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
2376
|
+
* @param options.onImplicitProp A callback invoked with the spread attribute node when an implicit prop is detected
|
|
2377
|
+
* @returns A rule listener reporting on JSX spread attributes
|
|
2378
|
+
*/
|
|
2379
|
+
function createImplicitPropListener(context, options) {
|
|
2380
|
+
const { name, isAllowedProp, isAllowedType, onImplicitProp } = options;
|
|
2381
|
+
const services = ESLintUtils.getParserServices(context, false);
|
|
2382
|
+
const checker = services.program.getTypeChecker();
|
|
2383
|
+
const getFqn = (symbol) => core.getFullyQualifiedNameEx(checker, symbol).toLowerCase();
|
|
2384
|
+
return { JSXSpreadAttribute(node) {
|
|
2385
|
+
for (const type of unionConstituents(getConstrainedTypeAtLocation(services, node.argument))) {
|
|
2386
|
+
const prop = type.getProperty(name);
|
|
2387
|
+
if (prop == null) continue;
|
|
2388
|
+
if (isAllowedProp(getFqn(prop))) continue;
|
|
2389
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
2390
|
+
const propTypeSymbol = propType.aliasSymbol ?? propType.symbol;
|
|
2391
|
+
if (propTypeSymbol != null && isAllowedType(getFqn(propTypeSymbol))) continue;
|
|
2392
|
+
onImplicitProp(node);
|
|
2393
|
+
}
|
|
2394
|
+
} };
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2380
2397
|
//#endregion
|
|
2381
2398
|
//#region src/rules/no-implicit-children/no-implicit-children.ts
|
|
2382
2399
|
const RULE_NAME$30 = "no-implicit-children";
|
|
@@ -2393,26 +2410,17 @@ var no_implicit_children_default = createRule({
|
|
|
2393
2410
|
defaultOptions: []
|
|
2394
2411
|
});
|
|
2395
2412
|
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
|
-
}
|
|
2413
|
+
return createImplicitPropListener(context, {
|
|
2414
|
+
name: "children",
|
|
2415
|
+
isAllowedProp: (fqn) => fqn.endsWith("attributes.children") || fqn.endsWith("propswithchildren.children"),
|
|
2416
|
+
isAllowedType: (fqn) => RE_REACT_CHILDREN_TYPE.test(fqn) || fqn === "jsx.element",
|
|
2417
|
+
onImplicitProp(node) {
|
|
2410
2418
|
context.report({
|
|
2411
2419
|
messageId: "default",
|
|
2412
2420
|
node
|
|
2413
2421
|
});
|
|
2414
2422
|
}
|
|
2415
|
-
}
|
|
2423
|
+
});
|
|
2416
2424
|
}
|
|
2417
2425
|
|
|
2418
2426
|
//#endregion
|
|
@@ -2430,23 +2438,17 @@ var no_implicit_key_default = createRule({
|
|
|
2430
2438
|
defaultOptions: []
|
|
2431
2439
|
});
|
|
2432
2440
|
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
|
-
}
|
|
2441
|
+
return createImplicitPropListener(context, {
|
|
2442
|
+
name: "key",
|
|
2443
|
+
isAllowedProp: (fqn) => fqn.endsWith("react.attributes.key"),
|
|
2444
|
+
isAllowedType: (fqn) => fqn.endsWith("react.key"),
|
|
2445
|
+
onImplicitProp(node) {
|
|
2444
2446
|
context.report({
|
|
2445
2447
|
messageId: "default",
|
|
2446
2448
|
node
|
|
2447
2449
|
});
|
|
2448
2450
|
}
|
|
2449
|
-
}
|
|
2451
|
+
});
|
|
2450
2452
|
}
|
|
2451
2453
|
|
|
2452
2454
|
//#endregion
|
|
@@ -2465,25 +2467,17 @@ var no_implicit_ref_default = createRule({
|
|
|
2465
2467
|
defaultOptions: []
|
|
2466
2468
|
});
|
|
2467
2469
|
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
|
-
}
|
|
2470
|
+
return createImplicitPropListener(context, {
|
|
2471
|
+
name: "ref",
|
|
2472
|
+
isAllowedProp: (fqn) => fqn.endsWith("attributes.ref"),
|
|
2473
|
+
isAllowedType: (fqn) => RE_REACT_REF_TYPE.test(fqn),
|
|
2474
|
+
onImplicitProp(node) {
|
|
2481
2475
|
context.report({
|
|
2482
2476
|
messageId: "default",
|
|
2483
2477
|
node
|
|
2484
2478
|
});
|
|
2485
2479
|
}
|
|
2486
|
-
}
|
|
2480
|
+
});
|
|
2487
2481
|
}
|
|
2488
2482
|
|
|
2489
2483
|
//#endregion
|
|
@@ -4682,6 +4676,7 @@ const SYNC_ARRAY_CALLBACKS = /* @__PURE__ */ new Set([
|
|
|
4682
4676
|
"sort"
|
|
4683
4677
|
]);
|
|
4684
4678
|
function createBindingResolver(context) {
|
|
4679
|
+
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
4685
4680
|
const bindings = /* @__PURE__ */ new Map();
|
|
4686
4681
|
const memberBindings = /* @__PURE__ */ new Map();
|
|
4687
4682
|
const jsxRefs = /* @__PURE__ */ new Set();
|
|
@@ -4701,7 +4696,7 @@ function createBindingResolver(context) {
|
|
|
4701
4696
|
kind: "function",
|
|
4702
4697
|
node: value
|
|
4703
4698
|
};
|
|
4704
|
-
if (value.type === AST_NODE_TYPES.CallExpression && (core.
|
|
4699
|
+
if (value.type === AST_NODE_TYPES.CallExpression && (core.isUseRefLikeCall(value, additionalRefHooks) || core.isCreateRefCall(context, value))) return { kind: "ref" };
|
|
4705
4700
|
if (value.type === AST_NODE_TYPES.MemberExpression && value.property.type === AST_NODE_TYPES.Identifier) {
|
|
4706
4701
|
if (isRefLikeName(value.property.name)) return { kind: "ref" };
|
|
4707
4702
|
}
|
|
@@ -7344,6 +7339,7 @@ function isHookDecl(node) {
|
|
|
7344
7339
|
return name != null && core.isHookName(name);
|
|
7345
7340
|
}
|
|
7346
7341
|
function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ */ new Set()) {
|
|
7342
|
+
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
7347
7343
|
if (seen.has(name)) return false;
|
|
7348
7344
|
seen.add(name);
|
|
7349
7345
|
for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
|
|
@@ -7352,7 +7348,7 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
|
|
|
7352
7348
|
if (init == null) continue;
|
|
7353
7349
|
switch (true) {
|
|
7354
7350
|
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.
|
|
7351
|
+
case init.type === AST_NODE_TYPES.CallExpression && core.isUseRefLikeCall(init, additionalRefHooks): return true;
|
|
7356
7352
|
case init.type === AST_NODE_TYPES.CallExpression: return getNestedIdentifiers(init).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id), seen));
|
|
7357
7353
|
}
|
|
7358
7354
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.18.1",
|
|
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/
|
|
50
|
-
"@eslint-react/jsx": "5.
|
|
51
|
-
"@eslint-react/
|
|
52
|
-
"@eslint-react/
|
|
53
|
-
"@eslint-react/var": "5.
|
|
48
|
+
"@eslint-react/ast": "5.18.1",
|
|
49
|
+
"@eslint-react/core": "5.18.1",
|
|
50
|
+
"@eslint-react/jsx": "5.18.1",
|
|
51
|
+
"@eslint-react/shared": "5.18.1",
|
|
52
|
+
"@eslint-react/eslint": "5.18.1",
|
|
53
|
+
"@eslint-react/var": "5.18.1"
|
|
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
|
-
"eslint": "^10.
|
|
60
|
-
"react": "^19.2.
|
|
61
|
-
"react-dom": "^19.2.
|
|
62
|
-
"tsdown": "^0.22.
|
|
59
|
+
"eslint": "^10.8.0",
|
|
60
|
+
"react": "^19.2.8",
|
|
61
|
+
"react-dom": "^19.2.8",
|
|
62
|
+
"tsdown": "^0.22.14",
|
|
63
63
|
"tsl": "^1.0.30",
|
|
64
64
|
"tsl-dx": "^0.13.3",
|
|
65
65
|
"typescript": "6.0.3",
|