oxlint-plugin-react-doctor 0.7.9-dev.95f9937 → 0.7.9-dev.a16e452
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 +34 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14751,6 +14751,29 @@ const getReleaseVerbName = (node) => {
|
|
|
14751
14751
|
}
|
|
14752
14752
|
return null;
|
|
14753
14753
|
};
|
|
14754
|
+
const isRetainedAbortControllerRefRelease = (releaseReceiver, usage, context) => {
|
|
14755
|
+
const releaseFunction = findEnclosingFunction$1(releaseReceiver);
|
|
14756
|
+
const usageFunction = findEnclosingFunction$1(usage.node);
|
|
14757
|
+
if (!releaseFunction || !usageFunction || !isFunctionLike$1(usageFunction) || !isReturnedEffectCleanupFunction(releaseFunction) || !hasReactRefCurrentOrigin(releaseReceiver, context.scopes)) return false;
|
|
14758
|
+
const controllerKey = getListenerAbortControllerKey(usage, context);
|
|
14759
|
+
const refCurrentKey = resolveExpressionKey(releaseReceiver, context);
|
|
14760
|
+
if (controllerKey === null || refCurrentKey === null) return false;
|
|
14761
|
+
const usageFunctionBody = usageFunction.body;
|
|
14762
|
+
const previousAbortCalls = [];
|
|
14763
|
+
const ownershipAssignments = [];
|
|
14764
|
+
walkAst(usageFunctionBody, (child) => {
|
|
14765
|
+
if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
|
|
14766
|
+
if (isNodeOfType(child, "AssignmentExpression") && resolveExpressionKey(child.left, context) === refCurrentKey && resolveExpressionKey(child.right, context) === controllerKey) {
|
|
14767
|
+
ownershipAssignments.push(child);
|
|
14768
|
+
return;
|
|
14769
|
+
}
|
|
14770
|
+
if (!isNodeOfType(child, "CallExpression")) return;
|
|
14771
|
+
const childCallee = isNodeOfType(child.callee, "ChainExpression") ? child.callee.expression : stripParenExpression(child.callee);
|
|
14772
|
+
if (isNodeOfType(childCallee, "MemberExpression") && !childCallee.computed && isNodeOfType(childCallee.property, "Identifier") && childCallee.property.name === "abort" && resolveExpressionKey(childCallee.object, context) === refCurrentKey) previousAbortCalls.push(child);
|
|
14773
|
+
});
|
|
14774
|
+
const safeOwnershipAssignments = ownershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, previousAbortCalls, usageFunction, context));
|
|
14775
|
+
return doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
|
|
14776
|
+
};
|
|
14754
14777
|
const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
14755
14778
|
const callNode = isNodeOfType(node, "ChainExpression") ? node.expression : node;
|
|
14756
14779
|
if (!isNodeOfType(callNode, "CallExpression")) return false;
|
|
@@ -14770,6 +14793,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
14770
14793
|
if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
|
|
14771
14794
|
if (usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName))) return true;
|
|
14772
14795
|
if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
|
|
14796
|
+
if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
|
|
14773
14797
|
if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
|
|
14774
14798
|
const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
|
|
14775
14799
|
if (!pairedVerbNames || !matchesPairedReleaseVerb(releaseVerbName, pairedVerbNames)) return false;
|
|
@@ -14803,9 +14827,11 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
14803
14827
|
return isNodeOfType(handlerArgument, "Literal") && handlerArgument.value === null;
|
|
14804
14828
|
}
|
|
14805
14829
|
if (releaseVerbName === "removeEventListener" || releaseVerbName === "removeListener" || releaseVerbName === "off") {
|
|
14806
|
-
const
|
|
14830
|
+
const usesUnaryListenerSignature = usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1 && callNode.arguments?.length === 1;
|
|
14831
|
+
const releaseHandler = usesUnaryListenerSignature ? callNode.arguments?.[0] : callNode.arguments?.[1];
|
|
14807
14832
|
if (!releaseHandler) return releaseVerbName === "off";
|
|
14808
|
-
|
|
14833
|
+
const expectedHandlerKey = usesUnaryListenerSignature ? usage.eventKey : usage.handlerKey;
|
|
14834
|
+
return expectedHandlerKey !== null && resolveExpressionKey(releaseHandler, context) === expectedHandlerKey;
|
|
14809
14835
|
}
|
|
14810
14836
|
if (releaseVerbName === "unobserve" && usage.eventKey !== null) return releaseEventKey === usage.eventKey;
|
|
14811
14837
|
return true;
|
|
@@ -14818,8 +14844,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
|
|
|
14818
14844
|
currentNode = parentNode;
|
|
14819
14845
|
parentNode = currentNode.parent;
|
|
14820
14846
|
}
|
|
14821
|
-
|
|
14822
|
-
const effectCallback = findEnclosingFunction$1(parentNode);
|
|
14847
|
+
const effectCallback = isNodeOfType(parentNode, "ReturnStatement") && parentNode.argument === currentNode ? findEnclosingFunction$1(parentNode) : isNodeOfType(parentNode, "ArrowFunctionExpression") && parentNode.body === currentNode ? parentNode : null;
|
|
14823
14848
|
const effectCall = effectCallback?.parent;
|
|
14824
14849
|
return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
|
|
14825
14850
|
};
|
|
@@ -15105,6 +15130,11 @@ const doesResourceResultEscape = (resourceNode, allowReturnedResourceEscape, all
|
|
|
15105
15130
|
parentNode = currentNode.parent;
|
|
15106
15131
|
continue;
|
|
15107
15132
|
}
|
|
15133
|
+
if (isNodeOfType(parentNode, "ConditionalExpression") && (parentNode.consequent === currentNode || parentNode.alternate === currentNode) || isNodeOfType(parentNode, "LogicalExpression") && (parentNode.right === currentNode || parentNode.left === currentNode && parentNode.operator !== "&&")) {
|
|
15134
|
+
currentNode = parentNode;
|
|
15135
|
+
parentNode = currentNode.parent;
|
|
15136
|
+
continue;
|
|
15137
|
+
}
|
|
15108
15138
|
if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode && isNodeOfType(parentNode.id, "Identifier") && isNodeOfType(parentNode.parent, "VariableDeclaration") && parentNode.parent.kind === "const") {
|
|
15109
15139
|
const ownerFunction = findEnclosingFunction$1(resourceNode);
|
|
15110
15140
|
const resourceSymbol = context.scopes.symbolFor(parentNode.id);
|