eslint-plugin-react-x 5.12.0 → 5.13.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 +109 -81
- package/package.json +7 -7
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.13.0";
|
|
148
148
|
|
|
149
149
|
//#endregion
|
|
150
150
|
//#region src/utils/create-rule.ts
|
|
@@ -1751,17 +1751,6 @@ function create$47(context) {
|
|
|
1751
1751
|
//#endregion
|
|
1752
1752
|
//#region src/rules/no-array-index-key/lib.ts
|
|
1753
1753
|
/**
|
|
1754
|
-
* Creates a report function for the given rule context.
|
|
1755
|
-
* @param context The ESLint rule context.
|
|
1756
|
-
* @returns A function that can be used to report violations.
|
|
1757
|
-
*/
|
|
1758
|
-
function report$2(context) {
|
|
1759
|
-
return (descriptor) => {
|
|
1760
|
-
if (descriptor == null) return;
|
|
1761
|
-
context.report(descriptor);
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
/**
|
|
1765
1754
|
* Iterator-like methods that pass the item's index to their callback,
|
|
1766
1755
|
* mapped to the position of the index parameter in the callback's parameter list.
|
|
1767
1756
|
* `map` and `forEach` also cover `Children.map` and `Children.forEach`,
|
|
@@ -1858,7 +1847,7 @@ function create$46(context) {
|
|
|
1858
1847
|
* @param node The key value expression to check.
|
|
1859
1848
|
* @returns The report descriptors for the violations found.
|
|
1860
1849
|
*/
|
|
1861
|
-
function
|
|
1850
|
+
function visitKeyExpression(node) {
|
|
1862
1851
|
switch (node.type) {
|
|
1863
1852
|
case AST_NODE_TYPES.Identifier: return isArrayIndex(node) ? [{
|
|
1864
1853
|
messageId: "default",
|
|
@@ -1872,8 +1861,8 @@ function create$46(context) {
|
|
|
1872
1861
|
messageId: "default",
|
|
1873
1862
|
node: identifier
|
|
1874
1863
|
}));
|
|
1875
|
-
case AST_NODE_TYPES.ConditionalExpression: return [...
|
|
1876
|
-
case AST_NODE_TYPES.LogicalExpression: return [...
|
|
1864
|
+
case AST_NODE_TYPES.ConditionalExpression: return [...visitKeyExpression(node.consequent), ...visitKeyExpression(node.alternate)];
|
|
1865
|
+
case AST_NODE_TYPES.LogicalExpression: return [...visitKeyExpression(node.left), ...visitKeyExpression(node.right)];
|
|
1877
1866
|
case AST_NODE_TYPES.CallExpression: {
|
|
1878
1867
|
const callee = Extract.unwrap(node.callee);
|
|
1879
1868
|
if (callee.type === AST_NODE_TYPES.MemberExpression && Extract.getPropertyName(callee.property) === "toString" && isArrayIndex(callee.object)) return [{
|
|
@@ -1898,12 +1887,12 @@ function create$46(context) {
|
|
|
1898
1887
|
for (const property of props.properties) {
|
|
1899
1888
|
const value = getKeyPropValue(property);
|
|
1900
1889
|
if (value == null) continue;
|
|
1901
|
-
|
|
1890
|
+
for (const desc of visitKeyExpression(value)) context.report(desc);
|
|
1902
1891
|
}
|
|
1903
1892
|
},
|
|
1904
1893
|
"JSXAttribute[name.name='key']"(node) {
|
|
1905
1894
|
if (node.value?.type !== AST_NODE_TYPES.JSXExpressionContainer) return;
|
|
1906
|
-
|
|
1895
|
+
for (const desc of visitKeyExpression(node.value.expression)) context.report(desc);
|
|
1907
1896
|
}
|
|
1908
1897
|
};
|
|
1909
1898
|
}
|
|
@@ -2599,7 +2588,7 @@ function create$28(context) {
|
|
|
2599
2588
|
* @param context The ESLint rule context.
|
|
2600
2589
|
* @returns A function that can be used to report violations.
|
|
2601
2590
|
*/
|
|
2602
|
-
function report
|
|
2591
|
+
function report(context) {
|
|
2603
2592
|
return (descriptor) => {
|
|
2604
2593
|
if (descriptor == null) return;
|
|
2605
2594
|
context.report(descriptor);
|
|
@@ -2643,16 +2632,16 @@ function create$27(context) {
|
|
|
2643
2632
|
* @param seen A set of visited identifier names to prevent infinite recursion.
|
|
2644
2633
|
* @returns A report descriptor if a problem is found, otherwise `null`
|
|
2645
2634
|
*/
|
|
2646
|
-
function
|
|
2635
|
+
function visit(node, seen = /* @__PURE__ */ new Set()) {
|
|
2647
2636
|
if (node == null) return null;
|
|
2648
|
-
if (Check.is(AST_NODE_TYPES.JSXExpressionContainer)(node)) return
|
|
2637
|
+
if (Check.is(AST_NODE_TYPES.JSXExpressionContainer)(node)) return visit(node.expression, seen);
|
|
2649
2638
|
if (Check.isJSX(node)) return null;
|
|
2650
|
-
if (Check.isTypeExpression(node)) return
|
|
2639
|
+
if (Check.isTypeExpression(node)) return visit(node.expression, seen);
|
|
2651
2640
|
return match(node).with({
|
|
2652
2641
|
type: AST_NODE_TYPES.LogicalExpression,
|
|
2653
2642
|
operator: "&&"
|
|
2654
2643
|
}, ({ left, right }) => {
|
|
2655
|
-
if (left.type === AST_NODE_TYPES.UnaryExpression && left.operator === "!") return
|
|
2644
|
+
if (left.type === AST_NODE_TYPES.UnaryExpression && left.operator === "!") return visit(right, seen);
|
|
2656
2645
|
const initialScope = context.sourceCode.getScope(left);
|
|
2657
2646
|
if (Check.isIdentifier(left, "NaN") || getStaticValue(left, initialScope)?.value === "NaN") return {
|
|
2658
2647
|
data: { value: context.sourceCode.getText(left) },
|
|
@@ -2661,22 +2650,22 @@ function create$27(context) {
|
|
|
2661
2650
|
};
|
|
2662
2651
|
const leftType = getConstrainedTypeAtLocation(services, left);
|
|
2663
2652
|
const leftTypeVariants = core.getTypeVariants(unionConstituents(leftType));
|
|
2664
|
-
if (Array.from(leftTypeVariants.values()).every((t) => allowedTypeVariants.some((a) => a === t))) return
|
|
2653
|
+
if (Array.from(leftTypeVariants.values()).every((t) => allowedTypeVariants.some((a) => a === t))) return visit(right, seen);
|
|
2665
2654
|
return {
|
|
2666
2655
|
data: { value: context.sourceCode.getText(left) },
|
|
2667
2656
|
messageId: "default",
|
|
2668
2657
|
node: left
|
|
2669
2658
|
};
|
|
2670
2659
|
}).with({ type: AST_NODE_TYPES.ConditionalExpression }, ({ alternate, consequent }) => {
|
|
2671
|
-
return
|
|
2660
|
+
return visit(consequent, seen) ?? visit(alternate, seen);
|
|
2672
2661
|
}).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
2673
2662
|
if (seen.has(n.name)) return null;
|
|
2674
2663
|
seen.add(n.name);
|
|
2675
2664
|
const variableDefNode = findVariable(context.sourceCode.getScope(n), n.name)?.defs.at(0)?.node;
|
|
2676
|
-
return match(variableDefNode).with({ init: P.select({ type: P.not(AST_NODE_TYPES.VariableDeclaration) }) }, (init) =>
|
|
2665
|
+
return match(variableDefNode).with({ init: P.select({ type: P.not(AST_NODE_TYPES.VariableDeclaration) }) }, (init) => visit(init, seen)).otherwise(() => null);
|
|
2677
2666
|
}).otherwise(() => null);
|
|
2678
2667
|
}
|
|
2679
|
-
return { JSXExpressionContainer: flow(
|
|
2668
|
+
return { JSXExpressionContainer: flow(visit, report(context)) };
|
|
2680
2669
|
}
|
|
2681
2670
|
|
|
2682
2671
|
//#endregion
|
|
@@ -2791,17 +2780,6 @@ const INDEX_PARAM_POSITIONS = /* @__PURE__ */ new Map([
|
|
|
2791
2780
|
["map", 0]
|
|
2792
2781
|
]);
|
|
2793
2782
|
/**
|
|
2794
|
-
* Creates a report function for the given rule context.
|
|
2795
|
-
* @param context The ESLint rule context.
|
|
2796
|
-
* @returns A function that can be used to report violations.
|
|
2797
|
-
*/
|
|
2798
|
-
function report(context) {
|
|
2799
|
-
return (descriptor) => {
|
|
2800
|
-
if (descriptor == null) return;
|
|
2801
|
-
context.report(descriptor);
|
|
2802
|
-
};
|
|
2803
|
-
}
|
|
2804
|
-
/**
|
|
2805
2783
|
* Gets the nested return statements in the node that are within the same function
|
|
2806
2784
|
* @param node The AST node
|
|
2807
2785
|
* @returns The nested return statements in the node
|
|
@@ -2857,7 +2835,7 @@ function create$24(context) {
|
|
|
2857
2835
|
* @param node The expression to check.
|
|
2858
2836
|
* @returns The report descriptors for the violations found.
|
|
2859
2837
|
*/
|
|
2860
|
-
function
|
|
2838
|
+
function visitItemExpression(node) {
|
|
2861
2839
|
switch (node.type) {
|
|
2862
2840
|
case AST_NODE_TYPES.JSXElement: return hasAttribute(context, node, "key") ? [] : [{
|
|
2863
2841
|
messageId: "default",
|
|
@@ -2867,8 +2845,8 @@ function create$24(context) {
|
|
|
2867
2845
|
messageId: "unexpectedFragmentSyntax",
|
|
2868
2846
|
node
|
|
2869
2847
|
}];
|
|
2870
|
-
case AST_NODE_TYPES.ConditionalExpression: return [...
|
|
2871
|
-
case AST_NODE_TYPES.LogicalExpression: return [...
|
|
2848
|
+
case AST_NODE_TYPES.ConditionalExpression: return [...visitItemExpression(node.consequent), ...visitItemExpression(node.alternate)];
|
|
2849
|
+
case AST_NODE_TYPES.LogicalExpression: return [...visitItemExpression(node.left), ...visitItemExpression(node.right)];
|
|
2872
2850
|
default: return [];
|
|
2873
2851
|
}
|
|
2874
2852
|
}
|
|
@@ -2878,16 +2856,16 @@ function create$24(context) {
|
|
|
2878
2856
|
* @param node The callback function to check.
|
|
2879
2857
|
* @returns The report descriptors for the violations found.
|
|
2880
2858
|
*/
|
|
2881
|
-
function
|
|
2882
|
-
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return
|
|
2883
|
-
return getNestedReturnStatements$1(node.body).flatMap((stmt) => stmt.argument == null ? [] :
|
|
2859
|
+
function visitIteratorCallback(node) {
|
|
2860
|
+
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return visitItemExpression(node.body);
|
|
2861
|
+
return getNestedReturnStatements$1(node.body).flatMap((stmt) => stmt.argument == null ? [] : visitItemExpression(stmt.argument));
|
|
2884
2862
|
}
|
|
2885
2863
|
return {
|
|
2886
2864
|
ArrayExpression(node) {
|
|
2887
2865
|
if (childrenToArrayDepth > 0) return;
|
|
2888
2866
|
for (const element of node.elements) {
|
|
2889
2867
|
if (element == null || element.type === AST_NODE_TYPES.SpreadElement) continue;
|
|
2890
|
-
|
|
2868
|
+
for (const desc of visitItemExpression(element)) context.report(desc);
|
|
2891
2869
|
}
|
|
2892
2870
|
},
|
|
2893
2871
|
CallExpression(node) {
|
|
@@ -2898,7 +2876,7 @@ function create$24(context) {
|
|
|
2898
2876
|
if (childrenToArrayDepth > 0) return;
|
|
2899
2877
|
const callback = getIteratorCallback(node);
|
|
2900
2878
|
if (callback == null) return;
|
|
2901
|
-
|
|
2879
|
+
for (const desc of visitIteratorCallback(callback)) context.report(desc);
|
|
2902
2880
|
},
|
|
2903
2881
|
"CallExpression:exit"(node) {
|
|
2904
2882
|
if (core.isChildrenToArrayCall(context, node)) childrenToArrayDepth -= 1;
|
|
@@ -4261,6 +4239,17 @@ function create$7(context) {
|
|
|
4261
4239
|
|
|
4262
4240
|
//#endregion
|
|
4263
4241
|
//#region src/rules/refs/lib.ts
|
|
4242
|
+
function isFunctionExpressionLike(node) {
|
|
4243
|
+
return node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression;
|
|
4244
|
+
}
|
|
4245
|
+
function resolveAlias(name, aliases) {
|
|
4246
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4247
|
+
while (aliases.has(name) && !seen.has(name)) {
|
|
4248
|
+
seen.add(name);
|
|
4249
|
+
name = aliases.get(name);
|
|
4250
|
+
}
|
|
4251
|
+
return name;
|
|
4252
|
+
}
|
|
4264
4253
|
/**
|
|
4265
4254
|
* Check if the node is the operand of a `ref.current === null` test inside an IfStatement.
|
|
4266
4255
|
* @param node The MemberExpression node for ref.current
|
|
@@ -4288,37 +4277,62 @@ function isInNullCheckTest(node) {
|
|
|
4288
4277
|
if (parent.type === AST_NODE_TYPES.UnaryExpression && parent.operator === "!") return isIfTest(parent);
|
|
4289
4278
|
return false;
|
|
4290
4279
|
}
|
|
4280
|
+
function isBinaryNullCheckOperand(a, b, refName) {
|
|
4281
|
+
a = Check.isTypeExpression(a) ? Extract.unwrap(a) : a;
|
|
4282
|
+
if (a.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
4283
|
+
if (Extract.getPropertyName(a.property) !== "current") return false;
|
|
4284
|
+
const obj = Extract.unwrap(a.object);
|
|
4285
|
+
return obj.type === AST_NODE_TYPES.Identifier && obj.name === refName && b.type === AST_NODE_TYPES.Literal && b.value == null;
|
|
4286
|
+
}
|
|
4291
4287
|
function isBinaryNullCheck(test, refName) {
|
|
4292
4288
|
if (test.type !== AST_NODE_TYPES.BinaryExpression) return false;
|
|
4293
4289
|
if (!/^(===|==|!==|!=)$/.test(test.operator)) return false;
|
|
4294
4290
|
const { left, right } = test;
|
|
4295
|
-
|
|
4296
|
-
a = Check.isTypeExpression(a) ? Extract.unwrap(a) : a;
|
|
4297
|
-
if (a.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
4298
|
-
if (Extract.getPropertyName(a.property) !== "current") return false;
|
|
4299
|
-
const obj = Extract.unwrap(a.object);
|
|
4300
|
-
return obj.type === AST_NODE_TYPES.Identifier && obj.name === refName && b.type === AST_NODE_TYPES.Literal && b.value == null;
|
|
4301
|
-
};
|
|
4302
|
-
return checkSides(left, right) || checkSides(right, left);
|
|
4291
|
+
return isBinaryNullCheckOperand(left, right, refName) || isBinaryNullCheckOperand(right, left, refName);
|
|
4303
4292
|
}
|
|
4304
4293
|
/**
|
|
4305
|
-
*
|
|
4306
|
-
*
|
|
4307
|
-
* `!(ref.current === null)`, and their != variants.
|
|
4294
|
+
* Determine which branch of an `if` statement is guaranteed to see `ref.current` as null, given
|
|
4295
|
+
* a test expression. Returns `null` if the test isn't a recognized null check on `refName`.
|
|
4308
4296
|
* @param test The test expression to check.
|
|
4309
4297
|
* @param refName The name of the ref variable.
|
|
4310
4298
|
*/
|
|
4311
|
-
function
|
|
4312
|
-
if (isBinaryNullCheck(test, refName)) return
|
|
4299
|
+
function getRefCurrentNullCheckBranch(test, refName) {
|
|
4300
|
+
if (isBinaryNullCheck(test, refName)) return test.operator === "===" || test.operator === "==" ? "consequent" : "alternate";
|
|
4313
4301
|
if (test.type === AST_NODE_TYPES.UnaryExpression && test.operator === "!") {
|
|
4314
4302
|
const arg = Extract.unwrap(test.argument);
|
|
4315
4303
|
if (arg.type === AST_NODE_TYPES.MemberExpression) {
|
|
4316
4304
|
const obj = Extract.unwrap(arg.object);
|
|
4317
|
-
return obj.type === AST_NODE_TYPES.Identifier && obj.name === refName && Extract.getPropertyName(arg.property) === "current";
|
|
4305
|
+
return obj.type === AST_NODE_TYPES.Identifier && obj.name === refName && Extract.getPropertyName(arg.property) === "current" ? "consequent" : null;
|
|
4306
|
+
}
|
|
4307
|
+
if (arg.type === AST_NODE_TYPES.BinaryExpression) {
|
|
4308
|
+
const inner = getRefCurrentNullCheckBranch(arg, refName);
|
|
4309
|
+
if (inner === "consequent") return "alternate";
|
|
4310
|
+
if (inner === "alternate") return "consequent";
|
|
4318
4311
|
}
|
|
4319
|
-
return isBinaryNullCheck(arg, refName);
|
|
4320
4312
|
}
|
|
4321
|
-
return
|
|
4313
|
+
return null;
|
|
4314
|
+
}
|
|
4315
|
+
/**
|
|
4316
|
+
* Check whether `node` (a `ref.current` MemberExpression) is being written to indirectly through
|
|
4317
|
+
* a nested property write, e.g. `ref.current.inner = value` or `ref.current.inner++`.
|
|
4318
|
+
* @param node The MemberExpression node for ref.current
|
|
4319
|
+
*/
|
|
4320
|
+
function isNestedRefCurrentWrite(node) {
|
|
4321
|
+
let outer = node;
|
|
4322
|
+
let sawNesting = false;
|
|
4323
|
+
while (true) {
|
|
4324
|
+
let parent = outer.parent;
|
|
4325
|
+
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
4326
|
+
if (parent.type === AST_NODE_TYPES.MemberExpression && (parent.object === outer || Extract.unwrap(parent.object) === outer)) {
|
|
4327
|
+
outer = parent;
|
|
4328
|
+
sawNesting = true;
|
|
4329
|
+
continue;
|
|
4330
|
+
}
|
|
4331
|
+
if (!sawNesting) return false;
|
|
4332
|
+
if (parent.type === AST_NODE_TYPES.AssignmentExpression) return parent.left === outer || Extract.unwrap(parent.left) === outer;
|
|
4333
|
+
if (parent.type === AST_NODE_TYPES.UpdateExpression) return parent.argument === outer || Extract.unwrap(parent.argument) === outer;
|
|
4334
|
+
return false;
|
|
4335
|
+
}
|
|
4322
4336
|
}
|
|
4323
4337
|
function isInitializedFromRef$1(context, name, initialScope) {
|
|
4324
4338
|
for (const { node } of findVariable(initialScope, name)?.defs ?? []) {
|
|
@@ -4356,17 +4370,6 @@ var refs_default = createRule({
|
|
|
4356
4370
|
create: create$6,
|
|
4357
4371
|
defaultOptions: []
|
|
4358
4372
|
});
|
|
4359
|
-
function resolveAlias(name, aliases) {
|
|
4360
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4361
|
-
while (aliases.has(name) && !seen.has(name)) {
|
|
4362
|
-
seen.add(name);
|
|
4363
|
-
name = aliases.get(name);
|
|
4364
|
-
}
|
|
4365
|
-
return name;
|
|
4366
|
-
}
|
|
4367
|
-
function isFunctionExpressionLike(node) {
|
|
4368
|
-
return node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression;
|
|
4369
|
-
}
|
|
4370
4373
|
/**
|
|
4371
4374
|
* Phase 2 (part): compute the set of functions that are "reached" during render for a given
|
|
4372
4375
|
* component/hook boundary. A function is reached if it is the boundary itself, or if it is
|
|
@@ -4400,10 +4403,10 @@ function computeReachedFunctions(boundary, isCompOrHookFn, functionVarBindings,
|
|
|
4400
4403
|
* i.e. every function boundary between the access and the component/hook `boundary` is itself
|
|
4401
4404
|
* a function that gets invoked during render (see `computeReachedFunctions`).
|
|
4402
4405
|
*/
|
|
4403
|
-
function isReachedDuringRender(node, boundary, reached) {
|
|
4406
|
+
function isReachedDuringRender(node, boundary, reached, alwaysReached) {
|
|
4404
4407
|
let current = node.parent;
|
|
4405
4408
|
while (current != null && current !== boundary) {
|
|
4406
|
-
if (Check.isFunction(current) && !reached.has(current)) return false;
|
|
4409
|
+
if (Check.isFunction(current) && !reached.has(current) && !alwaysReached.has(current)) return false;
|
|
4407
4410
|
current = current.parent;
|
|
4408
4411
|
}
|
|
4409
4412
|
return true;
|
|
@@ -4417,11 +4420,18 @@ function create$6(context) {
|
|
|
4417
4420
|
const refPassedToFunctions = [];
|
|
4418
4421
|
const functionVarBindings = /* @__PURE__ */ new Map();
|
|
4419
4422
|
const directCallSites = [];
|
|
4423
|
+
const stateInitializerFunctions = /* @__PURE__ */ new Set();
|
|
4420
4424
|
return merge(hc.visitor, fc.visitor, {
|
|
4421
4425
|
AssignmentExpression(node) {
|
|
4422
4426
|
if (node.operator !== "=") return;
|
|
4423
4427
|
const left = Extract.unwrap(node.left);
|
|
4424
4428
|
const right = Extract.unwrap(node.right);
|
|
4429
|
+
if (left.type === AST_NODE_TYPES.MemberExpression) {
|
|
4430
|
+
const leftObj = Extract.unwrap(left.object);
|
|
4431
|
+
const propName = Extract.getPropertyName(left.property);
|
|
4432
|
+
if (leftObj.type === AST_NODE_TYPES.Identifier && propName != null && isFunctionExpressionLike(right)) functionVarBindings.set(`${leftObj.name}.${propName}`, right);
|
|
4433
|
+
return;
|
|
4434
|
+
}
|
|
4425
4435
|
if (left.type !== AST_NODE_TYPES.Identifier) return;
|
|
4426
4436
|
if (right.type === AST_NODE_TYPES.Identifier) {
|
|
4427
4437
|
aliases.set(left.name, right.name);
|
|
@@ -4436,7 +4446,21 @@ function create$6(context) {
|
|
|
4436
4446
|
calleeName: callee.name,
|
|
4437
4447
|
node
|
|
4438
4448
|
});
|
|
4439
|
-
if (
|
|
4449
|
+
else if (callee.type === AST_NODE_TYPES.MemberExpression) {
|
|
4450
|
+
const calleeObj = Extract.unwrap(callee.object);
|
|
4451
|
+
if (calleeObj.type === AST_NODE_TYPES.Identifier && calleeName != null) directCallSites.push({
|
|
4452
|
+
calleeName: `${calleeObj.name}.${calleeName}`,
|
|
4453
|
+
node
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
if (calleeName === "useState") {
|
|
4457
|
+
const [firstArg] = node.arguments;
|
|
4458
|
+
if (firstArg != null) {
|
|
4459
|
+
const unwrappedFirstArg = Extract.unwrap(firstArg);
|
|
4460
|
+
if (isFunctionExpressionLike(unwrappedFirstArg)) stateInitializerFunctions.add(unwrappedFirstArg);
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
if (calleeName != null && (calleeName.startsWith("use") || calleeName === "mergeRefs" || calleeName === "render")) return;
|
|
4440
4464
|
for (const arg of node.arguments) {
|
|
4441
4465
|
const unwrapped = Extract.unwrap(arg);
|
|
4442
4466
|
if (unwrapped.type !== AST_NODE_TYPES.Identifier) continue;
|
|
@@ -4462,7 +4486,7 @@ function create$6(context) {
|
|
|
4462
4486
|
isWrite: (() => {
|
|
4463
4487
|
let parent = node.parent;
|
|
4464
4488
|
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
4465
|
-
return match(parent).with({ type: AST_NODE_TYPES.AssignmentExpression }, (p) => p.left === node || Extract.unwrap(p.left) === node).with({ type: AST_NODE_TYPES.UpdateExpression }, (p) => p.argument === node || Extract.unwrap(p.argument) === node).otherwise(() => false);
|
|
4489
|
+
return match(parent).with({ type: AST_NODE_TYPES.AssignmentExpression }, (p) => p.left === node || Extract.unwrap(p.left) === node).with({ type: AST_NODE_TYPES.UpdateExpression }, (p) => p.argument === node || Extract.unwrap(p.argument) === node).otherwise(() => false) || isNestedRefCurrentWrite(node);
|
|
4466
4490
|
})(),
|
|
4467
4491
|
node
|
|
4468
4492
|
});
|
|
@@ -4501,16 +4525,19 @@ function create$6(context) {
|
|
|
4501
4525
|
} else continue;
|
|
4502
4526
|
const boundary = Traverse.findParent(node, isCompOrHookFn);
|
|
4503
4527
|
if (boundary == null) continue;
|
|
4504
|
-
if (!isReachedDuringRender(node, boundary, getReached(boundary))) continue;
|
|
4528
|
+
if (!isReachedDuringRender(node, boundary, getReached(boundary), stateInitializerFunctions)) continue;
|
|
4505
4529
|
let isLazyInit = refName != null && isInNullCheckTest(node);
|
|
4506
4530
|
let matchedGuardIf = false;
|
|
4507
4531
|
if (!isLazyInit && refName != null) {
|
|
4508
4532
|
let current = node.parent;
|
|
4533
|
+
let prevChild = node;
|
|
4509
4534
|
findLoop: while (true) {
|
|
4510
4535
|
if (current.type === AST_NODE_TYPES.IfStatement) {
|
|
4511
|
-
|
|
4512
|
-
|
|
4536
|
+
const nullBranch = getRefCurrentNullCheckBranch(current.test, refName);
|
|
4537
|
+
const actualBranch = current.consequent === prevChild ? "consequent" : current.alternate === prevChild ? "alternate" : null;
|
|
4538
|
+
if (nullBranch != null && actualBranch != null) {
|
|
4513
4539
|
matchedGuardIf = true;
|
|
4540
|
+
isLazyInit = actualBranch === nullBranch ? isWrite : !isWrite;
|
|
4514
4541
|
}
|
|
4515
4542
|
break;
|
|
4516
4543
|
}
|
|
@@ -4530,6 +4557,7 @@ function create$6(context) {
|
|
|
4530
4557
|
case AST_NODE_TYPES.CallExpression: break;
|
|
4531
4558
|
default: break findLoop;
|
|
4532
4559
|
}
|
|
4560
|
+
prevChild = current;
|
|
4533
4561
|
current = current.parent;
|
|
4534
4562
|
}
|
|
4535
4563
|
}
|
|
@@ -4542,7 +4570,7 @@ function create$6(context) {
|
|
|
4542
4570
|
if (stmtIdx >= 0) for (let i = stmtIdx - 1; i >= 0; i--) {
|
|
4543
4571
|
const sibling = block.body[i];
|
|
4544
4572
|
if (sibling == null) continue;
|
|
4545
|
-
if (sibling.type === AST_NODE_TYPES.IfStatement &&
|
|
4573
|
+
if (sibling.type === AST_NODE_TYPES.IfStatement && getRefCurrentNullCheckBranch(sibling.test, refName) === "alternate") {
|
|
4546
4574
|
isLazyInit = true;
|
|
4547
4575
|
break;
|
|
4548
4576
|
}
|
|
@@ -4570,7 +4598,7 @@ function create$6(context) {
|
|
|
4570
4598
|
for (const { node } of refPassedToFunctions) {
|
|
4571
4599
|
const boundary = Traverse.findParent(node, isCompOrHookFn);
|
|
4572
4600
|
if (boundary == null) continue;
|
|
4573
|
-
if (!isReachedDuringRender(node, boundary, getReached(boundary))) continue;
|
|
4601
|
+
if (!isReachedDuringRender(node, boundary, getReached(boundary), stateInitializerFunctions)) continue;
|
|
4574
4602
|
context.report({
|
|
4575
4603
|
messageId: "refPassedToFunction",
|
|
4576
4604
|
node
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-x",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.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,12 +45,12 @@
|
|
|
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/
|
|
49
|
-
"@eslint-react/
|
|
50
|
-
"@eslint-react/eslint": "5.
|
|
51
|
-
"@eslint-react/
|
|
52
|
-
"@eslint-react/
|
|
53
|
-
"@eslint-react/
|
|
48
|
+
"@eslint-react/core": "5.13.0",
|
|
49
|
+
"@eslint-react/ast": "5.13.0",
|
|
50
|
+
"@eslint-react/eslint": "5.13.0",
|
|
51
|
+
"@eslint-react/var": "5.13.0",
|
|
52
|
+
"@eslint-react/jsx": "5.13.0",
|
|
53
|
+
"@eslint-react/shared": "5.13.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/react": "^19.2.17",
|