oxlint-plugin-react-doctor 0.8.3-dev.416fdcc → 0.8.3-dev.76263ec
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 +30 -70
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8262,56 +8262,6 @@ const isNodeReachableWithinFunction = (node, context) => {
|
|
|
8262
8262
|
return false;
|
|
8263
8263
|
};
|
|
8264
8264
|
//#endregion
|
|
8265
|
-
//#region src/plugin/utils/is-proven-non-throwing-built-in-call.ts
|
|
8266
|
-
const NON_THROWING_CONSOLE_METHOD_NAMES = new Set([
|
|
8267
|
-
"debug",
|
|
8268
|
-
"error",
|
|
8269
|
-
"info",
|
|
8270
|
-
"log",
|
|
8271
|
-
"trace",
|
|
8272
|
-
"warn"
|
|
8273
|
-
]);
|
|
8274
|
-
const NON_THROWING_NUMBER_BINARY_OPERATORS = new Set([
|
|
8275
|
-
"+",
|
|
8276
|
-
"-",
|
|
8277
|
-
"*",
|
|
8278
|
-
"/",
|
|
8279
|
-
"%",
|
|
8280
|
-
"**"
|
|
8281
|
-
]);
|
|
8282
|
-
const isGlobalPerformanceNowCall = (callNode, scopes) => {
|
|
8283
|
-
const callee = stripParenExpression(callNode.callee);
|
|
8284
|
-
if (!isNodeOfType(callee, "MemberExpression") || callNode.arguments.length !== 0) return false;
|
|
8285
|
-
const receiver = stripParenExpression(callee.object);
|
|
8286
|
-
return isNodeOfType(receiver, "Identifier") && receiver.name === "performance" && scopes.isGlobalReference(receiver) && getStaticPropertyName(callee) === "now";
|
|
8287
|
-
};
|
|
8288
|
-
const isProvenNonThrowingNumberExpression = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
8289
|
-
const strippedExpression = stripParenExpression(expression);
|
|
8290
|
-
if (isNodeOfType(strippedExpression, "Literal")) return typeof strippedExpression.value === "number";
|
|
8291
|
-
if (isNodeOfType(strippedExpression, "CallExpression")) return isGlobalPerformanceNowCall(strippedExpression, scopes);
|
|
8292
|
-
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
8293
|
-
const symbol = scopes.symbolFor(strippedExpression);
|
|
8294
|
-
if (symbol?.kind !== "const" || !symbol.initializer || symbol.declarationNode.range[0] >= strippedExpression.range[0] || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
8295
|
-
visitedSymbolIds.add(symbol.id);
|
|
8296
|
-
return isProvenNonThrowingNumberExpression(symbol.initializer, scopes, visitedSymbolIds);
|
|
8297
|
-
}
|
|
8298
|
-
if (isNodeOfType(strippedExpression, "UnaryExpression")) return (strippedExpression.operator === "+" || strippedExpression.operator === "-") && isProvenNonThrowingNumberExpression(strippedExpression.argument, scopes, visitedSymbolIds);
|
|
8299
|
-
if (!isNodeOfType(strippedExpression, "BinaryExpression")) return false;
|
|
8300
|
-
if (!NON_THROWING_NUMBER_BINARY_OPERATORS.has(strippedExpression.operator)) return false;
|
|
8301
|
-
return isProvenNonThrowingNumberExpression(strippedExpression.left, scopes, new Set(visitedSymbolIds)) && isProvenNonThrowingNumberExpression(strippedExpression.right, scopes, new Set(visitedSymbolIds));
|
|
8302
|
-
};
|
|
8303
|
-
const isProvenNonThrowingBuiltInCall = (callNode, scopes) => {
|
|
8304
|
-
if (isGlobalPerformanceNowCall(callNode, scopes)) return true;
|
|
8305
|
-
const callee = stripParenExpression(callNode.callee);
|
|
8306
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
8307
|
-
const receiver = stripParenExpression(callee.object);
|
|
8308
|
-
if (!isNodeOfType(receiver, "Identifier") || !scopes.isGlobalReference(receiver)) return false;
|
|
8309
|
-
const methodName = getStaticPropertyName(callee);
|
|
8310
|
-
if (receiver.name === "console") return NON_THROWING_CONSOLE_METHOD_NAMES.has(methodName ?? "");
|
|
8311
|
-
const firstArgument = callNode.arguments[0];
|
|
8312
|
-
return Boolean(receiver.name === "Math" && methodName === "round" && callNode.arguments.length === 1 && firstArgument && isProvenNonThrowingNumberExpression(firstArgument, scopes));
|
|
8313
|
-
};
|
|
8314
|
-
//#endregion
|
|
8315
8265
|
//#region src/plugin/utils/serialize-reference-key.ts
|
|
8316
8266
|
const serializeReferenceKey = ({ node, scopes }) => {
|
|
8317
8267
|
const expression = stripParenExpression(node);
|
|
@@ -9089,10 +9039,11 @@ const functionHasPotentialSynchronousThrow = (functionNode, classBody, scopes, b
|
|
|
9089
9039
|
}
|
|
9090
9040
|
if (!isNodeOfType(candidate, "CallExpression")) return;
|
|
9091
9041
|
if (cleanupReleaseKeys(candidate, scopes, classBody).length > 0) return;
|
|
9092
|
-
if (isProvenNonThrowingBuiltInCall(candidate, scopes)) return;
|
|
9093
9042
|
const callee = stripParenExpression(candidate.callee);
|
|
9094
9043
|
if (isNodeOfType(callee, "MemberExpression")) {
|
|
9095
|
-
|
|
9044
|
+
const receiver = stripParenExpression(callee.object);
|
|
9045
|
+
if (isNodeOfType(receiver, "Identifier") && receiver.name === "console" && scopes.isGlobalReference(receiver)) return;
|
|
9046
|
+
if (isNodeOfType(receiver, "ThisExpression")) {
|
|
9096
9047
|
const memberName = getStaticPropertyName(callee);
|
|
9097
9048
|
const memberFunction = memberName ? classMemberFunction(classBody, memberName, candidate) : null;
|
|
9098
9049
|
if (memberFunction && !functionHasPotentialSynchronousThrow(memberFunction, classBody, scopes, Number.POSITIVE_INFINITY, new Set(visitedFunctions))) return;
|
|
@@ -18064,7 +18015,7 @@ const hasPotentialInterruptionAfterGuard = (callback, guardState, usageNode, con
|
|
|
18064
18015
|
if (child !== callback.body && isFunctionLike$1(child)) return false;
|
|
18065
18016
|
const childStart = getRangeStart(child);
|
|
18066
18017
|
if (childStart === null || childStart <= guardStart || childStart >= usageStart) return;
|
|
18067
|
-
if (isNodeOfType(child, "
|
|
18018
|
+
if (isNodeOfType(child, "CallExpression") || isNodeOfType(child, "AwaitExpression") || isNodeOfType(child, "YieldExpression")) {
|
|
18068
18019
|
if (canNodeReachLaterNodeWithinFunction(child, usageNode, callback, context) || canInterruptionReachUsageThroughCatch(child, usageNode, callback, context)) {
|
|
18069
18020
|
hasPotentialInterruption = true;
|
|
18070
18021
|
return false;
|
|
@@ -18230,7 +18181,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
|
|
|
18230
18181
|
for (const argument of usage.node.arguments ?? []) walkAst(argument, (argumentChild) => {
|
|
18231
18182
|
if (hasPotentialInterruption) return false;
|
|
18232
18183
|
if (isFunctionLike$1(argumentChild)) return false;
|
|
18233
|
-
if (isNodeOfType(argumentChild, "
|
|
18184
|
+
if (isNodeOfType(argumentChild, "CallExpression") || isNodeOfType(argumentChild, "AwaitExpression") || isNodeOfType(argumentChild, "YieldExpression")) {
|
|
18234
18185
|
hasPotentialInterruption = true;
|
|
18235
18186
|
return false;
|
|
18236
18187
|
}
|
|
@@ -63458,9 +63409,10 @@ const chainCarriesRejectionHandler = (node, scopes) => {
|
|
|
63458
63409
|
}
|
|
63459
63410
|
if (isNodeOfType(child, "CallExpression")) {
|
|
63460
63411
|
const callee = stripParenExpression(child.callee);
|
|
63461
|
-
const
|
|
63412
|
+
const receiver = isNodeOfType(callee, "MemberExpression") ? stripParenExpression(callee.object) : null;
|
|
63413
|
+
const isConsoleCall = isNodeOfType(receiver, "Identifier") && receiver.name === "console" && (!scopes || scopes.isGlobalReference(receiver));
|
|
63462
63414
|
const localFunction = scopes && isNodeOfType(callee, "Identifier") ? resolveExactLocalFunction(callee, scopes) : null;
|
|
63463
|
-
if (!
|
|
63415
|
+
if (!isConsoleCall && !isPromiseResolveCall(child, scopes) && !chainCarriesRejectionHandler(child, scopes) && (!localFunction || !scopes || subtreeCanThrowSynchronously(localFunction, localFunction, scopes))) {
|
|
63464
63416
|
canReject = true;
|
|
63465
63417
|
return false;
|
|
63466
63418
|
}
|
|
@@ -63937,7 +63889,6 @@ const helperHasUnhandledSynchronousCall = (helper, depth, scopes, visitedFunctio
|
|
|
63937
63889
|
ancestor = ancestor.parent ?? null;
|
|
63938
63890
|
}
|
|
63939
63891
|
if (isInsideNonRethrowingTry(child, helper)) return;
|
|
63940
|
-
if (scopes && isProvenNonThrowingBuiltInCall(child, scopes)) return;
|
|
63941
63892
|
if (isPromiseResolveCall(child, scopes) || chainCarriesRejectionHandler(child, scopes) || isSyncArrayLiteralMethodCall(child, scopes) || isThunkActionDispatchCall(child) || isNeverRejectingPromiseCombinatorCall(child, depth, scopes)) return;
|
|
63942
63893
|
const callee = stripParenExpression(child.callee);
|
|
63943
63894
|
if (scopes && isNodeOfType(callee, "Identifier") && isReactHookResultReference(callee, STATE_HOOK_NAMES, 1, scopes)) return;
|
|
@@ -63947,6 +63898,7 @@ const helperHasUnhandledSynchronousCall = (helper, depth, scopes, visitedFunctio
|
|
|
63947
63898
|
}
|
|
63948
63899
|
if (isNodeOfType(callee, "MemberExpression")) {
|
|
63949
63900
|
const receiver = stripParenExpression(callee.object);
|
|
63901
|
+
if (isNodeOfType(receiver, "Identifier") && receiver.name === "console" && (!scopes || scopes.isGlobalReference(receiver))) return;
|
|
63950
63902
|
if (getStaticPropertyName(callee) === "push" && isNodeOfType(receiver, "Identifier")) {
|
|
63951
63903
|
const receiverSymbol = scopes?.symbolFor(receiver);
|
|
63952
63904
|
if (isNodeOfType(receiverSymbol?.initializer ? stripParenExpression(receiverSymbol.initializer) : null, "ArrayExpression") && receiverSymbol?.references.every((reference) => {
|
|
@@ -64416,20 +64368,16 @@ const areOnExclusiveBranches = (first, second, functionNode) => {
|
|
|
64416
64368
|
};
|
|
64417
64369
|
const REACT_SETTER_CALLEE_PATTERN = /^set[A-Z]/;
|
|
64418
64370
|
const isProvenNonThrowingSynchronousCall = (callNode, context) => {
|
|
64419
|
-
if (isProvenNonThrowingBuiltInCall(callNode, context.scopes)) return true;
|
|
64420
64371
|
const callee = stripParenExpression(callNode.callee);
|
|
64421
64372
|
if (isNodeOfType(callee, "Identifier")) {
|
|
64422
64373
|
if (isReactHookResultReference(callee, STATE_HOOK_NAMES, 1, context.scopes) || context.scopes.isGlobalReference(callee) && REACT_SETTER_CALLEE_PATTERN.test(callee.name)) return true;
|
|
64423
|
-
if (context.scopes.isGlobalReference(callee) && callee.name === "String") {
|
|
64424
|
-
const firstArgument = callNode.arguments[0];
|
|
64425
|
-
const strippedArgument = firstArgument ? stripParenExpression(firstArgument) : null;
|
|
64426
|
-
return Boolean(callNode.arguments.length === 1 && strippedArgument && isNodeOfType(strippedArgument, "Identifier") && context.scopes.symbolFor(strippedArgument)?.kind === "catch-clause-parameter");
|
|
64427
|
-
}
|
|
64428
64374
|
const localFunction = resolveExactLocalFunction(callee, context.scopes);
|
|
64429
64375
|
if (localFunction && isFunctionLike$1(localFunction) && !localFunction.async) return !subtreeCanThrowSynchronously(localFunction, localFunction, context.scopes) && !helperHasUnhandledSynchronousCall(localFunction, NEVER_REJECTING_ANALYSIS_MAX_DEPTH, context.scopes);
|
|
64430
64376
|
return false;
|
|
64431
64377
|
}
|
|
64432
|
-
return false;
|
|
64378
|
+
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
64379
|
+
const receiver = stripParenExpression(callee.object);
|
|
64380
|
+
return Boolean(isNodeOfType(receiver, "Identifier") && receiver.name === "console" && context.scopes.isGlobalReference(receiver));
|
|
64433
64381
|
};
|
|
64434
64382
|
const subtreeHasAbruptSynchronousOperation = (root, functionBoundary, context) => {
|
|
64435
64383
|
let canCompleteAbruptly = false;
|
|
@@ -73658,6 +73606,14 @@ const REJECTING_PROMISE_COMBINATOR_NAMES = new Set([
|
|
|
73658
73606
|
const MAX_INITIATOR_RESOLUTION_DEPTH = 3;
|
|
73659
73607
|
const STATE_DISPATCHER_HOOK_NAMES = new Set(["useState", "useReducer"]);
|
|
73660
73608
|
const REF_HOOK_NAMES = new Set(["useRef"]);
|
|
73609
|
+
const NON_REJECTING_CONSOLE_METHOD_NAMES = new Set([
|
|
73610
|
+
"debug",
|
|
73611
|
+
"error",
|
|
73612
|
+
"info",
|
|
73613
|
+
"log",
|
|
73614
|
+
"trace",
|
|
73615
|
+
"warn"
|
|
73616
|
+
]);
|
|
73661
73617
|
const MESSAGE$26 = "This promise chain runs in an effect, ends in a `.then` that sets state or mutates a ref, and has no `.catch` or enclosing try/catch, so a rejection leaves the state unset and surfaces as an unhandled rejection. Add a `.catch` handler on the chain (`.finally` does not count).";
|
|
73662
73618
|
const isKnownNonThenableHandlerReturn = (expression, context, visitedBindingIdentifiers = /* @__PURE__ */ new Set()) => {
|
|
73663
73619
|
const strippedExpression = stripParenExpression(expression);
|
|
@@ -73694,10 +73650,6 @@ const isKnownNonRejectingHandler = (argument, context) => {
|
|
|
73694
73650
|
return false;
|
|
73695
73651
|
}
|
|
73696
73652
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
73697
|
-
if (isProvenNonThrowingBuiltInCall(child, context.scopes)) {
|
|
73698
|
-
didFindKnownNonRejectingCall = true;
|
|
73699
|
-
return;
|
|
73700
|
-
}
|
|
73701
73653
|
const callee = stripParenExpression(child.callee);
|
|
73702
73654
|
if (isNodeOfType(callee, "Identifier") && isReactHookResultReference(callee, STATE_DISPATCHER_HOOK_NAMES, 1, context.scopes)) {
|
|
73703
73655
|
if (context.scopes.symbolFor(callee)?.references.every((reference) => reference.flag === "read")) {
|
|
@@ -73705,6 +73657,13 @@ const isKnownNonRejectingHandler = (argument, context) => {
|
|
|
73705
73657
|
return;
|
|
73706
73658
|
}
|
|
73707
73659
|
}
|
|
73660
|
+
if (isNodeOfType(callee, "MemberExpression")) {
|
|
73661
|
+
const receiver = stripParenExpression(callee.object);
|
|
73662
|
+
if (isNodeOfType(receiver, "Identifier") && receiver.name === "console" && context.scopes.isGlobalReference(receiver) && NON_REJECTING_CONSOLE_METHOD_NAMES.has(getStaticPropertyName(callee) ?? "")) {
|
|
73663
|
+
didFindKnownNonRejectingCall = true;
|
|
73664
|
+
return;
|
|
73665
|
+
}
|
|
73666
|
+
}
|
|
73708
73667
|
canReject = true;
|
|
73709
73668
|
return false;
|
|
73710
73669
|
});
|
|
@@ -73713,10 +73672,11 @@ const isKnownNonRejectingHandler = (argument, context) => {
|
|
|
73713
73672
|
const isTrustedNonThrowingMethodCallee = (member, context) => {
|
|
73714
73673
|
const parent = member.parent;
|
|
73715
73674
|
if (!parent || !isNodeOfType(parent, "CallExpression") || parent.callee !== member) return false;
|
|
73716
|
-
if (isProvenNonThrowingBuiltInCall(parent, context.scopes)) return true;
|
|
73717
73675
|
const receiver = stripParenExpression(member.object);
|
|
73718
73676
|
if (!isNodeOfType(receiver, "Identifier") || !context.scopes.isGlobalReference(receiver)) return false;
|
|
73719
|
-
|
|
73677
|
+
const methodName = getStaticPropertyName(member);
|
|
73678
|
+
if (receiver.name === "console") return NON_REJECTING_CONSOLE_METHOD_NAMES.has(methodName ?? "");
|
|
73679
|
+
return receiver.name === "Promise" && methodName === "resolve";
|
|
73720
73680
|
};
|
|
73721
73681
|
const handlerHasPotentiallyThrowingMemberRead = (argument, context) => {
|
|
73722
73682
|
if (!argument) return false;
|