eslint-plugin-react-x 5.22.1 → 5.23.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 +101 -64
- package/package.json +12 -12
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.
|
|
147
|
+
var version = "5.23.1";
|
|
148
148
|
|
|
149
149
|
//#endregion
|
|
150
150
|
//#region src/utils/create-rule.ts
|
|
@@ -1125,7 +1125,7 @@ function getUnknownDependenciesMessage(reactiveHookName) {
|
|
|
1125
1125
|
}
|
|
1126
1126
|
|
|
1127
1127
|
//#endregion
|
|
1128
|
-
//#region src/rules/globals/
|
|
1128
|
+
//#region src/rules/globals/helpers.ts
|
|
1129
1129
|
/**
|
|
1130
1130
|
* Array methods that mutate the array in place.
|
|
1131
1131
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
@@ -1154,7 +1154,7 @@ function resolveToFunction(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
|
1154
1154
|
|
|
1155
1155
|
//#endregion
|
|
1156
1156
|
//#region src/rules/globals/collect.ts
|
|
1157
|
-
function createFactCollector$
|
|
1157
|
+
function createFactCollector$3() {
|
|
1158
1158
|
const facts = {
|
|
1159
1159
|
callEdges: [],
|
|
1160
1160
|
methodCalls: [],
|
|
@@ -1355,7 +1355,7 @@ var globals_default = createRule({
|
|
|
1355
1355
|
function create$49(context) {
|
|
1356
1356
|
const hooks = core.getHookCollector(context);
|
|
1357
1357
|
const comps = core.getFunctionComponentCollector(context);
|
|
1358
|
-
const facts = createFactCollector$
|
|
1358
|
+
const facts = createFactCollector$3();
|
|
1359
1359
|
return merge(hooks.visitor, comps.visitor, facts.visitor, { "Program:exit"(program) {
|
|
1360
1360
|
const renderFunctions = [...comps.api.getAllComponents(program), ...hooks.api.getAllHooks(program)].map(({ node }) => node);
|
|
1361
1361
|
const directEffects = inferGlobalMutations(context, facts.facts);
|
|
@@ -1375,7 +1375,7 @@ function create$49(context) {
|
|
|
1375
1375
|
}
|
|
1376
1376
|
|
|
1377
1377
|
//#endregion
|
|
1378
|
-
//#region src/rules/immutability/
|
|
1378
|
+
//#region src/rules/immutability/helpers.ts
|
|
1379
1379
|
/**
|
|
1380
1380
|
* Methods that mutate their receiver in place.
|
|
1381
1381
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
@@ -1481,7 +1481,7 @@ function isRefLikeChain(context, node) {
|
|
|
1481
1481
|
|
|
1482
1482
|
//#endregion
|
|
1483
1483
|
//#region src/rules/immutability/collect.ts
|
|
1484
|
-
function createFactCollector$
|
|
1484
|
+
function createFactCollector$2() {
|
|
1485
1485
|
const facts = {
|
|
1486
1486
|
mutations: [],
|
|
1487
1487
|
sinks: []
|
|
@@ -1742,7 +1742,7 @@ var immutability_default = createRule({
|
|
|
1742
1742
|
function create$48(context) {
|
|
1743
1743
|
const hooks = core.getHookCollector(context);
|
|
1744
1744
|
const comps = core.getFunctionComponentCollector(context);
|
|
1745
|
-
const facts = createFactCollector$
|
|
1745
|
+
const facts = createFactCollector$2();
|
|
1746
1746
|
return merge(hooks.visitor, comps.visitor, facts.visitor, { "Program:exit"(program) {
|
|
1747
1747
|
for (const hook of hooks.api.getAllHooks(program)) for (const expression of hook.rets) if (expression != null) facts.facts.sinks.push({
|
|
1748
1748
|
kind: "hook-return",
|
|
@@ -5080,7 +5080,7 @@ function create$7(context) {
|
|
|
5080
5080
|
}
|
|
5081
5081
|
|
|
5082
5082
|
//#endregion
|
|
5083
|
-
//#region src/rules/refs/
|
|
5083
|
+
//#region src/rules/refs/helpers.ts
|
|
5084
5084
|
const SYNC_ARRAY_CALLBACKS = /* @__PURE__ */ new Set([
|
|
5085
5085
|
"every",
|
|
5086
5086
|
"filter",
|
|
@@ -5272,7 +5272,7 @@ function isAfterTerminatingNonNullGuard(node, getNullBranch) {
|
|
|
5272
5272
|
* reachable during render and are not collected. All inference is deferred to
|
|
5273
5273
|
* `Program:exit`.
|
|
5274
5274
|
*/
|
|
5275
|
-
function createFactCollector() {
|
|
5275
|
+
function createFactCollector$1() {
|
|
5276
5276
|
const facts = {
|
|
5277
5277
|
bindings: [],
|
|
5278
5278
|
callEdges: [],
|
|
@@ -5719,7 +5719,7 @@ var refs_default = createRule({
|
|
|
5719
5719
|
function create$6(context) {
|
|
5720
5720
|
const hooks = core.getHookCollector(context);
|
|
5721
5721
|
const comps = core.getFunctionComponentCollector(context);
|
|
5722
|
-
const facts = createFactCollector();
|
|
5722
|
+
const facts = createFactCollector$1();
|
|
5723
5723
|
return merge(hooks.visitor, comps.visitor, facts.visitor, { "Program:exit"(program) {
|
|
5724
5724
|
const boundaries = /* @__PURE__ */ new Set([...comps.api.getAllComponents(program).map((component) => component.node), ...hooks.api.getAllHooks(program).map((hook) => hook.node)]);
|
|
5725
5725
|
const resolver = createBindingResolver(context, facts.facts);
|
|
@@ -8389,36 +8389,67 @@ function create$4(context) {
|
|
|
8389
8389
|
}
|
|
8390
8390
|
|
|
8391
8391
|
//#endregion
|
|
8392
|
-
//#region src/rules/static-components/
|
|
8392
|
+
//#region src/rules/static-components/collect.ts
|
|
8393
|
+
function createFactCollector() {
|
|
8394
|
+
const facts = { componentUsages: [] };
|
|
8395
|
+
return {
|
|
8396
|
+
facts,
|
|
8397
|
+
visitor: { JSXOpeningElement(node) {
|
|
8398
|
+
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier) return;
|
|
8399
|
+
const name = node.name.name;
|
|
8400
|
+
if (!core.isFunctionComponentName(name)) return;
|
|
8401
|
+
facts.componentUsages.push({
|
|
8402
|
+
name,
|
|
8403
|
+
node: node.name
|
|
8404
|
+
});
|
|
8405
|
+
} }
|
|
8406
|
+
};
|
|
8407
|
+
}
|
|
8408
|
+
|
|
8409
|
+
//#endregion
|
|
8410
|
+
//#region src/rules/static-components/helpers.ts
|
|
8393
8411
|
/**
|
|
8394
8412
|
* Expression kinds that always produce a brand-new value on every evaluation, and are
|
|
8395
8413
|
* therefore considered "dynamically created" when used as a component's value.
|
|
8396
8414
|
*/
|
|
8397
|
-
const
|
|
8415
|
+
const KNOWN_DYNAMIC_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
8398
8416
|
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
8399
8417
|
AST_NODE_TYPES.CallExpression,
|
|
8400
8418
|
AST_NODE_TYPES.ClassExpression,
|
|
8401
8419
|
AST_NODE_TYPES.FunctionExpression,
|
|
8402
8420
|
AST_NODE_TYPES.NewExpression
|
|
8403
8421
|
]);
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8422
|
+
/**
|
|
8423
|
+
* Builds a predicate that tells whether a given node lies within the body of one of the
|
|
8424
|
+
* collected function or class components, i.e. whether it is "created during render".
|
|
8425
|
+
*/
|
|
8426
|
+
function createRenderBoundaryChecker(componentNodes) {
|
|
8427
|
+
const components = new Set(componentNodes);
|
|
8428
|
+
return (node) => Traverse.findParent(node, (n) => (Check.isFunction(n) || Check.isClass(n)) && components.has(n)) != null;
|
|
8407
8429
|
}
|
|
8430
|
+
|
|
8431
|
+
//#endregion
|
|
8432
|
+
//#region src/rules/static-components/origins.ts
|
|
8408
8433
|
/**
|
|
8409
8434
|
* Resolves an expression down to the node that dynamically created its value, if any.
|
|
8410
|
-
* Recurses through identifier references
|
|
8435
|
+
* Recurses through identifier references, both branches of ternaries and logical
|
|
8436
|
+
* expressions, and the final element of sequence expressions.
|
|
8411
8437
|
*/
|
|
8412
8438
|
function findDynamicCreationSite(context, node, isInsideRender, seen) {
|
|
8413
8439
|
const expr = Extract.unwrap(node);
|
|
8414
|
-
if (
|
|
8440
|
+
if (KNOWN_DYNAMIC_EXPRESSION_TYPES.has(expr.type)) return expr;
|
|
8415
8441
|
switch (expr.type) {
|
|
8416
8442
|
case AST_NODE_TYPES.ConditionalExpression: return findDynamicCreationSite(context, expr.consequent, isInsideRender, seen) ?? findDynamicCreationSite(context, expr.alternate, isInsideRender, seen);
|
|
8443
|
+
case AST_NODE_TYPES.LogicalExpression: return findDynamicCreationSite(context, expr.left, isInsideRender, seen) ?? findDynamicCreationSite(context, expr.right, isInsideRender, seen);
|
|
8444
|
+
case AST_NODE_TYPES.SequenceExpression: {
|
|
8445
|
+
const last = expr.expressions.at(-1);
|
|
8446
|
+
return last == null ? null : findDynamicCreationSite(context, last, isInsideRender, seen);
|
|
8447
|
+
}
|
|
8417
8448
|
case AST_NODE_TYPES.Identifier:
|
|
8418
8449
|
case AST_NODE_TYPES.JSXIdentifier: {
|
|
8419
|
-
const variable =
|
|
8450
|
+
const variable = findVariable(context.sourceCode.getScope(expr), expr.name);
|
|
8420
8451
|
if (variable == null) return null;
|
|
8421
|
-
return
|
|
8452
|
+
return resolveDynamicComponentOrigin(context, variable, isInsideRender, seen).creationNode;
|
|
8422
8453
|
}
|
|
8423
8454
|
default: return null;
|
|
8424
8455
|
}
|
|
@@ -8437,7 +8468,11 @@ function findReassignmentCreationSite(context, variable, isInsideRender, seen) {
|
|
|
8437
8468
|
}
|
|
8438
8469
|
return null;
|
|
8439
8470
|
}
|
|
8440
|
-
|
|
8471
|
+
/**
|
|
8472
|
+
* Resolves a component binding to the node that dynamically created its value, if the
|
|
8473
|
+
* value is created during render (a fresh function/class/call result on every render).
|
|
8474
|
+
*/
|
|
8475
|
+
function resolveDynamicComponentOrigin(context, variable, isInsideRender, seen = /* @__PURE__ */ new Set()) {
|
|
8441
8476
|
if (seen.has(variable)) return {
|
|
8442
8477
|
creationNode: null,
|
|
8443
8478
|
isDynamic: false
|
|
@@ -8446,7 +8481,7 @@ function getDynamicComponentSource(context, variable, isInsideRender, seen = /*
|
|
|
8446
8481
|
for (const def of variable.defs) {
|
|
8447
8482
|
const defNode = def.node;
|
|
8448
8483
|
if (!isInsideRender(defNode)) continue;
|
|
8449
|
-
if (
|
|
8484
|
+
if (def.type === DefinitionType.FunctionName || def.type === DefinitionType.ClassName) return {
|
|
8450
8485
|
creationNode: defNode,
|
|
8451
8486
|
isDynamic: true
|
|
8452
8487
|
};
|
|
@@ -8468,6 +8503,32 @@ function getDynamicComponentSource(context, variable, isInsideRender, seen = /*
|
|
|
8468
8503
|
};
|
|
8469
8504
|
}
|
|
8470
8505
|
|
|
8506
|
+
//#endregion
|
|
8507
|
+
//#region src/rules/static-components/effects.ts
|
|
8508
|
+
/**
|
|
8509
|
+
* Turn collected JSX component usages into effects: a usage is an effect when the
|
|
8510
|
+
* binding it refers to is declared inside a component's render body and its value is
|
|
8511
|
+
* dynamically created, i.e. the component is recreated on every render.
|
|
8512
|
+
*/
|
|
8513
|
+
function inferCreatedComponents(context, componentUsages, isInsideRender) {
|
|
8514
|
+
const effects = [];
|
|
8515
|
+
for (const usage of componentUsages) {
|
|
8516
|
+
const variable = findVariable(context.sourceCode.getScope(usage.node), usage.name);
|
|
8517
|
+
if (variable == null) continue;
|
|
8518
|
+
const def = variable.defs.at(0);
|
|
8519
|
+
if (def == null) continue;
|
|
8520
|
+
if (!isInsideRender(def.node)) continue;
|
|
8521
|
+
const { creationNode, isDynamic } = resolveDynamicComponentOrigin(context, variable, isInsideRender);
|
|
8522
|
+
if (!isDynamic) continue;
|
|
8523
|
+
effects.push({
|
|
8524
|
+
name: usage.name,
|
|
8525
|
+
creationNode,
|
|
8526
|
+
node: usage.node
|
|
8527
|
+
});
|
|
8528
|
+
}
|
|
8529
|
+
return effects;
|
|
8530
|
+
}
|
|
8531
|
+
|
|
8471
8532
|
//#endregion
|
|
8472
8533
|
//#region src/rules/static-components/static-components.ts
|
|
8473
8534
|
const RULE_NAME$3 = "static-components";
|
|
@@ -8489,51 +8550,27 @@ function create$3(context) {
|
|
|
8489
8550
|
const hint = core.FunctionComponentDetectionHint.DoNotIncludeJsxWithNumberValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithBooleanValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithNullValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithStringValue | core.FunctionComponentDetectionHint.DoNotIncludeJsxWithUndefinedValue | core.FunctionComponentDetectionHint.RequireBothSidesOfLogicalExpressionToBeJsx | core.FunctionComponentDetectionHint.RequireBothBranchesOfConditionalExpressionToBeJsx | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayPatternElement | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayExpressionElement | core.FunctionComponentDetectionHint.DoNotIncludeFunctionDefinedAsArrayMapCallback;
|
|
8490
8551
|
const fc = core.getFunctionComponentCollector(context, { hint });
|
|
8491
8552
|
const cc = core.getClassComponentCollector(context);
|
|
8492
|
-
const
|
|
8493
|
-
return merge(fc.visitor, cc.visitor, {
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
name,
|
|
8500
|
-
|
|
8553
|
+
const facts = createFactCollector();
|
|
8554
|
+
return merge(fc.visitor, cc.visitor, facts.visitor, { "Program:exit"(program) {
|
|
8555
|
+
const componentNodes = [...fc.api.getAllComponents(program), ...cc.api.getAllComponents(program)].map((component) => component.node);
|
|
8556
|
+
const isInsideRender = createRenderBoundaryChecker(componentNodes);
|
|
8557
|
+
const reportedCreations = /* @__PURE__ */ new Set();
|
|
8558
|
+
for (const effect of inferCreatedComponents(context, facts.facts.componentUsages, isInsideRender)) {
|
|
8559
|
+
context.report({
|
|
8560
|
+
data: { name: effect.name },
|
|
8561
|
+
messageId: "default",
|
|
8562
|
+
node: effect.node
|
|
8501
8563
|
});
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8564
|
+
if (effect.creationNode != null && !reportedCreations.has(effect.creationNode)) {
|
|
8565
|
+
reportedCreations.add(effect.creationNode);
|
|
8566
|
+
context.report({
|
|
8567
|
+
data: { name: effect.name },
|
|
8568
|
+
messageId: "createdHere",
|
|
8569
|
+
node: effect.creationNode
|
|
8570
|
+
});
|
|
8571
|
+
}
|
|
8506
8572
|
}
|
|
8507
|
-
});
|
|
8508
|
-
}
|
|
8509
|
-
/**
|
|
8510
|
-
* Builds a predicate that tells whether a given node lies within the body of a
|
|
8511
|
-
* previously-collected function or class component, i.e. whether it is "created during render".
|
|
8512
|
-
*/
|
|
8513
|
-
function createRenderBoundaryChecker(fc, cc, program) {
|
|
8514
|
-
const componentNodes = new Set([...fc.api.getAllComponents(program), ...cc.api.getAllComponents(program)].map((component) => component.node));
|
|
8515
|
-
const getEnclosingComponent = (node) => Traverse.findParent(node, (n) => (Check.isFunction(n) || Check.isClass(n)) && componentNodes.has(n));
|
|
8516
|
-
return (node) => getEnclosingComponent(node) != null;
|
|
8517
|
-
}
|
|
8518
|
-
function reportIfCreatedDuringRender(context, candidate, isInsideRender) {
|
|
8519
|
-
const { name, identifier } = candidate;
|
|
8520
|
-
const variable = findVariableForIdentifier(context, identifier);
|
|
8521
|
-
if (variable == null) return;
|
|
8522
|
-
const def = variable.defs.at(0);
|
|
8523
|
-
if (def == null) return;
|
|
8524
|
-
if (!isInsideRender(def.node)) return;
|
|
8525
|
-
const { creationNode, isDynamic } = getDynamicComponentSource(context, variable, isInsideRender);
|
|
8526
|
-
if (!isDynamic) return;
|
|
8527
|
-
context.report({
|
|
8528
|
-
data: { name },
|
|
8529
|
-
messageId: "default",
|
|
8530
|
-
node: identifier
|
|
8531
|
-
});
|
|
8532
|
-
if (creationNode != null) context.report({
|
|
8533
|
-
data: { name },
|
|
8534
|
-
messageId: "createdHere",
|
|
8535
|
-
node: creationNode
|
|
8536
|
-
});
|
|
8573
|
+
} });
|
|
8537
8574
|
}
|
|
8538
8575
|
|
|
8539
8576
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.23.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,16 +36,16 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@eslint-react/ast": "5.
|
|
40
|
-
"@eslint-react/core": "5.
|
|
41
|
-
"@eslint-react/eslint": "5.
|
|
42
|
-
"@eslint-react/jsx": "5.
|
|
43
|
-
"@eslint-react/shared": "5.
|
|
44
|
-
"@eslint-react/var": "5.
|
|
45
|
-
"@typescript-eslint/scope-manager": "^8.
|
|
46
|
-
"@typescript-eslint/types": "^8.
|
|
47
|
-
"@typescript-eslint/typescript-estree": "^8.
|
|
48
|
-
"@typescript-eslint/utils": "^8.
|
|
39
|
+
"@eslint-react/ast": "5.23.1",
|
|
40
|
+
"@eslint-react/core": "5.23.1",
|
|
41
|
+
"@eslint-react/eslint": "5.23.1",
|
|
42
|
+
"@eslint-react/jsx": "5.23.1",
|
|
43
|
+
"@eslint-react/shared": "5.23.1",
|
|
44
|
+
"@eslint-react/var": "5.23.1",
|
|
45
|
+
"@typescript-eslint/scope-manager": "^8.71.0",
|
|
46
|
+
"@typescript-eslint/types": "^8.71.0",
|
|
47
|
+
"@typescript-eslint/typescript-estree": "^8.71.0",
|
|
48
|
+
"@typescript-eslint/utils": "^8.71.0",
|
|
49
49
|
"compare-versions": "^6.1.1",
|
|
50
50
|
"string-ts": "^2.3.1",
|
|
51
51
|
"ts-api-utils": "^2.5.0",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"react-dom": "^19.3.0",
|
|
63
63
|
"tsdown": "^0.23.0",
|
|
64
64
|
"tsl": "^1.0.30",
|
|
65
|
-
"tsl-dx": "^0.13.
|
|
65
|
+
"tsl-dx": "^0.13.7",
|
|
66
66
|
"typescript": "6.0.3"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|