oxlint-plugin-react-doctor 0.4.1 → 0.4.2-dev.42c339f
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/LICENSE +1 -1
- package/dist/index.js +20 -20
- package/package.json +1 -1
package/LICENSE
CHANGED
package/dist/index.js
CHANGED
|
@@ -14445,6 +14445,14 @@ const isSynchronous = (node, within) => {
|
|
|
14445
14445
|
if (isNodeOfType(node, "AwaitExpression") || isNodeOfType(node, "UnaryExpression") && node.operator === "void" || isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ArrowFunctionExpression")) return false;
|
|
14446
14446
|
return isSynchronous(node.parent, within);
|
|
14447
14447
|
};
|
|
14448
|
+
const resolveToFunction = (ref) => {
|
|
14449
|
+
const definitionNode = ref.resolved?.defs[0]?.node;
|
|
14450
|
+
if (!definitionNode) return null;
|
|
14451
|
+
if (isFunctionLike$2(definitionNode)) return definitionNode;
|
|
14452
|
+
if (isNodeOfType(definitionNode, "VariableDeclarator") && isFunctionLike$2(definitionNode.init)) return definitionNode.init;
|
|
14453
|
+
return null;
|
|
14454
|
+
};
|
|
14455
|
+
const resolvesToAsyncFunction = (ref) => Boolean(resolveToFunction(ref)?.async);
|
|
14448
14456
|
const isEventualCallTo = (analysis, ref, predicate) => {
|
|
14449
14457
|
const callExprRefs = [];
|
|
14450
14458
|
ascend(analysis, ref, (upRef) => {
|
|
@@ -14571,12 +14579,8 @@ const getEffectFn = (analysis, node) => {
|
|
|
14571
14579
|
if (!fn) return null;
|
|
14572
14580
|
if (isNodeOfType(fn, "ArrowFunctionExpression") || isNodeOfType(fn, "FunctionExpression")) return fn;
|
|
14573
14581
|
if (isNodeOfType(fn, "Identifier")) {
|
|
14574
|
-
const
|
|
14575
|
-
|
|
14576
|
-
if (definitionNode && isNodeOfType(definitionNode, "VariableDeclarator")) {
|
|
14577
|
-
const initializer = definitionNode.init;
|
|
14578
|
-
if (isNodeOfType(initializer, "ArrowFunctionExpression") || isNodeOfType(initializer, "FunctionExpression")) return initializer;
|
|
14579
|
-
}
|
|
14582
|
+
const ref = getRef(analysis, fn);
|
|
14583
|
+
return ref ? resolveToFunction(ref) : null;
|
|
14580
14584
|
}
|
|
14581
14585
|
return null;
|
|
14582
14586
|
};
|
|
@@ -14657,6 +14661,7 @@ const isRefCurrent = (ref) => {
|
|
|
14657
14661
|
return parent.property.name === "current";
|
|
14658
14662
|
};
|
|
14659
14663
|
const isStateSetterCall = (analysis, ref) => isEventualCallTo(analysis, ref, (innerRef) => isStateSetter(analysis, innerRef));
|
|
14664
|
+
const isSyncStateSetterCall = (analysis, ref, effectFn) => isStateSetterCall(analysis, ref) && isSynchronous(ref.identifier, effectFn) && !resolvesToAsyncFunction(ref);
|
|
14660
14665
|
const isPropCall = (analysis, ref) => isEventualCallTo(analysis, ref, (innerRef) => isPropAlias(analysis, innerRef));
|
|
14661
14666
|
const isRefCall = (analysis, ref) => isEventualCallTo(analysis, ref, (innerRef) => isRefCurrent(innerRef) || isRef(analysis, innerRef));
|
|
14662
14667
|
const getUseStateDecl = (analysis, ref) => {
|
|
@@ -14668,12 +14673,8 @@ const isCleanupReturnArgument = (analysis, node) => {
|
|
|
14668
14673
|
if (isFunctionLike$2(node)) return true;
|
|
14669
14674
|
if (isNodeOfType(node, "MemberExpression")) return true;
|
|
14670
14675
|
if (isNodeOfType(node, "Identifier")) {
|
|
14671
|
-
const
|
|
14672
|
-
if (
|
|
14673
|
-
if (definitionNode && isNodeOfType(definitionNode, "VariableDeclarator")) {
|
|
14674
|
-
const initializer = definitionNode.init;
|
|
14675
|
-
return isFunctionLike$2(initializer);
|
|
14676
|
-
}
|
|
14676
|
+
const ref = getRef(analysis, node);
|
|
14677
|
+
if (ref && resolveToFunction(ref)) return true;
|
|
14677
14678
|
}
|
|
14678
14679
|
if (isNodeOfType(node, "ConditionalExpression")) return isCleanupReturnArgument(analysis, node.consequent) || isCleanupReturnArgument(analysis, node.alternate);
|
|
14679
14680
|
return false;
|
|
@@ -14723,8 +14724,7 @@ const noAdjustStateOnPropChange = defineRule({
|
|
|
14723
14724
|
if (!effectFn) return;
|
|
14724
14725
|
if (!depsRefs.flatMap((ref) => getUpstreamRefs(analysis, ref)).some((ref) => isProp(analysis, ref))) return;
|
|
14725
14726
|
for (const ref of effectFnRefs) {
|
|
14726
|
-
if (!
|
|
14727
|
-
if (!isSynchronous(ref.identifier, effectFn)) continue;
|
|
14727
|
+
if (!isSyncStateSetterCall(analysis, ref, effectFn)) continue;
|
|
14728
14728
|
const callExpr = getCallExpr(ref);
|
|
14729
14729
|
if (!callExpr) continue;
|
|
14730
14730
|
if (getArgsUpstreamRefs(analysis, ref).some((argRef) => isProp(analysis, argRef))) continue;
|
|
@@ -15631,8 +15631,7 @@ const noChainStateUpdates = defineRule({
|
|
|
15631
15631
|
if (!effectFn) return;
|
|
15632
15632
|
if (!depsRefs.flatMap((ref) => getUpstreamRefs(analysis, ref)).some((ref) => isState(analysis, ref))) return;
|
|
15633
15633
|
for (const ref of effectFnRefs) {
|
|
15634
|
-
if (!
|
|
15635
|
-
if (!isSynchronous(ref.identifier, effectFn)) continue;
|
|
15634
|
+
if (!isSyncStateSetterCall(analysis, ref, effectFn)) continue;
|
|
15636
15635
|
const callExpr = getCallExpr(ref);
|
|
15637
15636
|
if (!callExpr) continue;
|
|
15638
15637
|
if (getArgsUpstreamRefs(analysis, ref).some((argRef) => isState(analysis, argRef))) continue;
|
|
@@ -16250,8 +16249,7 @@ const noDerivedState = defineRule({
|
|
|
16250
16249
|
const effectFn = getEffectFn(analysis, node);
|
|
16251
16250
|
if (!effectFn) return;
|
|
16252
16251
|
for (const ref of effectFnRefs) {
|
|
16253
|
-
if (!
|
|
16254
|
-
if (!isSynchronous(ref.identifier, effectFn)) continue;
|
|
16252
|
+
if (!isSyncStateSetterCall(analysis, ref, effectFn)) continue;
|
|
16255
16253
|
const callExpr = getCallExpr(ref);
|
|
16256
16254
|
if (!callExpr) continue;
|
|
16257
16255
|
const stateName = getStateNameForUseStateDecl(getUseStateDecl(analysis, ref)) ?? "<state>";
|
|
@@ -18640,8 +18638,7 @@ const noInitializeState = defineRule({
|
|
|
18640
18638
|
if (!effectFn) return;
|
|
18641
18639
|
if (!(depsRefs.filter((ref) => !isStateSetter(analysis, ref)).length === 0)) return;
|
|
18642
18640
|
for (const ref of effectFnRefs) {
|
|
18643
|
-
if (!
|
|
18644
|
-
if (!isSynchronous(ref.identifier, effectFn)) continue;
|
|
18641
|
+
if (!isSyncStateSetterCall(analysis, ref, effectFn)) continue;
|
|
18645
18642
|
const callExpr = getCallExpr(ref);
|
|
18646
18643
|
if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
|
|
18647
18644
|
const useStateDecl = getUseStateDecl(analysis, ref);
|
|
@@ -27868,6 +27865,7 @@ const rnListCallbackPerRow = defineRule({
|
|
|
27868
27865
|
tags: ["test-noise"],
|
|
27869
27866
|
requires: ["react-native"],
|
|
27870
27867
|
severity: "warn",
|
|
27868
|
+
disabledBy: ["react-compiler"],
|
|
27871
27869
|
recommendation: "Move the handler out with useCallback at list scope and pass the row id as a prop. Then memo() rows skip redrawing when their data has not changed.",
|
|
27872
27870
|
create: (context) => {
|
|
27873
27871
|
const inspect = (node) => {
|
|
@@ -28304,6 +28302,7 @@ const rnNoInlineFlatlistRenderitem = defineRule({
|
|
|
28304
28302
|
tags: ["test-noise"],
|
|
28305
28303
|
requires: ["react-native"],
|
|
28306
28304
|
severity: "warn",
|
|
28305
|
+
disabledBy: ["react-compiler"],
|
|
28307
28306
|
recommendation: "Move renderItem to a named function or wrap it in useCallback so it is not rebuilt every time the screen redraws.",
|
|
28308
28307
|
create: (context) => ({ JSXAttribute(node) {
|
|
28309
28308
|
if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "renderItem") return;
|
|
@@ -28328,6 +28327,7 @@ const rnNoInlineObjectInListItem = defineRule({
|
|
|
28328
28327
|
tags: ["test-noise"],
|
|
28329
28328
|
requires: ["react-native"],
|
|
28330
28329
|
severity: "warn",
|
|
28330
|
+
disabledBy: ["react-compiler"],
|
|
28331
28331
|
recommendation: "Move style and object props out of renderItem (StyleSheet.create, useMemo at list scope, or pass primitives) so memo() rows stop redrawing when their data has not changed.",
|
|
28332
28332
|
create: (context) => {
|
|
28333
28333
|
const renderPropStack = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxlint-plugin-react-doctor",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2-dev.42c339f",
|
|
4
4
|
"description": "oxlint plugin for React Doctor: diagnose React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|