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.
Files changed (2) hide show
  1. package/dist/index.js +30 -70
  2. 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
- if (isNodeOfType(stripParenExpression(callee.object), "ThisExpression")) {
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, "AwaitExpression") || isNodeOfType(child, "YieldExpression") || isNodeOfType(child, "CallExpression") && !isProvenNonThrowingBuiltInCall(child, context.scopes)) {
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, "AwaitExpression") || isNodeOfType(argumentChild, "YieldExpression") || isNodeOfType(argumentChild, "CallExpression") && !isProvenNonThrowingBuiltInCall(argumentChild, context.scopes)) {
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 isKnownNonThrowingBuiltInCall = Boolean(scopes && isProvenNonThrowingBuiltInCall(child, scopes));
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 (!isKnownNonThrowingBuiltInCall && !isPromiseResolveCall(child, scopes) && !chainCarriesRejectionHandler(child, scopes) && (!localFunction || !scopes || subtreeCanThrowSynchronously(localFunction, localFunction, scopes))) {
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
- return receiver.name === "Promise" && getStaticPropertyName(member) === "resolve";
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.8.3-dev.416fdcc",
3
+ "version": "0.8.3-dev.76263ec",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",