oxlint-plugin-react-doctor 0.7.9-dev.c7f61cd → 0.7.9-dev.c88a39a

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 +34 -225
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -579,22 +579,6 @@ const EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS = new Set([
579
579
  "ResizeObserver",
580
580
  "PerformanceObserver"
581
581
  ]);
582
- const EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES = new Set([
583
- "blur",
584
- "focus",
585
- "getBoundingClientRect",
586
- "getClientRects",
587
- "measure",
588
- "measureInWindow",
589
- "measureLayout",
590
- "scroll",
591
- "scrollBy",
592
- "scrollIntoView",
593
- "scrollTo",
594
- "select",
595
- "setRangeText",
596
- "setSelectionRange"
597
- ]);
598
582
  const STORAGE_OBJECTS = new Set(["localStorage", "sessionStorage"]);
599
583
  //#endregion
600
584
  //#region src/plugin/constants/react.ts
@@ -28485,11 +28469,7 @@ const isProvenNativeReadMethod = (ref, methodName) => Boolean(ref.resolved?.defs
28485
28469
  }));
28486
28470
  //#endregion
28487
28471
  //#region src/plugin/rules/state-and-effects/utils/effect/react.ts
28488
- const KNOWN_COMPONENT_WRAPPER_NAMES = new Set([
28489
- "memo",
28490
- "forwardRef",
28491
- "observer"
28492
- ]);
28472
+ const KNOWN_PURE_HOC_NAMES = new Set(["memo", "forwardRef"]);
28493
28473
  const startsWithUppercase = (name) => Boolean(name && name.length > 0 && name[0] >= "A" && name[0] <= "Z");
28494
28474
  const isReactFunctionalComponent = (node) => {
28495
28475
  if (!node) return false;
@@ -28511,7 +28491,7 @@ const isReactFunctionalHOC = (analysis, node) => {
28511
28491
  const isWrappedInline = () => {
28512
28492
  if (!isNodeOfType(init, "CallExpression")) return false;
28513
28493
  if (!isNodeOfType(init.callee, "Identifier")) return false;
28514
- if (KNOWN_COMPONENT_WRAPPER_NAMES.has(init.callee.name)) return false;
28494
+ if (KNOWN_PURE_HOC_NAMES.has(init.callee.name)) return false;
28515
28495
  const firstArg = init.arguments?.[0];
28516
28496
  if (!firstArg) return false;
28517
28497
  return isNodeOfType(firstArg, "ArrowFunctionExpression") || isNodeOfType(firstArg, "FunctionExpression");
@@ -28531,7 +28511,7 @@ const isReactFunctionalHOC = (analysis, node) => {
28531
28511
  if (!args.includes(refId)) continue;
28532
28512
  const callee = parent.callee;
28533
28513
  const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "CallExpression") && isNodeOfType(callee.callee, "Identifier") ? callee.callee.name : null;
28534
- if (calleeName != null && !KNOWN_COMPONENT_WRAPPER_NAMES.has(calleeName)) return true;
28514
+ if (calleeName != null && !KNOWN_PURE_HOC_NAMES.has(calleeName)) return true;
28535
28515
  }
28536
28516
  return false;
28537
28517
  };
@@ -32959,18 +32939,6 @@ const isProvenIntrinsicJsxElement = (openingElement, scopes) => {
32959
32939
  return isIntrinsicValue(openingElement.name);
32960
32940
  };
32961
32941
  //#endregion
32962
- //#region src/plugin/utils/is-inline-intrinsic-ref-callback.ts
32963
- const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
32964
- const functionExpression = findTransparentExpressionRoot(functionNode);
32965
- if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
32966
- const container = functionExpression.parent;
32967
- if (!container || !isNodeOfType(container, "JSXExpressionContainer")) return false;
32968
- const attribute = container.parent;
32969
- if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
32970
- const openingElement = attribute.parent;
32971
- return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
32972
- };
32973
- //#endregion
32974
32942
  //#region src/plugin/rules/react-builtins/is-safe-create-ref-callback-current-write.ts
32975
32943
  const pathStartsWith$1 = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32976
32944
  const collectMemberExpression = (identifier) => {
@@ -32981,6 +32949,16 @@ const collectMemberExpression = (identifier) => {
32981
32949
  }
32982
32950
  return expression;
32983
32951
  };
32952
+ const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
32953
+ const functionExpression = findTransparentExpressionRoot(functionNode);
32954
+ if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
32955
+ const container = functionExpression.parent;
32956
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer")) return false;
32957
+ const attribute = container.parent;
32958
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
32959
+ const openingElement = attribute.parent;
32960
+ return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
32961
+ };
32984
32962
  const isSafeCreateRefCallbackCurrentWrite = (referenceNode, accessedPropertyPath, targetPropertyPath, scopes) => {
32985
32963
  if (accessedPropertyPath.length !== targetPropertyPath.length + 1 || !pathStartsWith$1(accessedPropertyPath, targetPropertyPath) || accessedPropertyPath[targetPropertyPath.length] !== "current") return false;
32986
32964
  const memberExpression = collectMemberExpression(referenceNode);
@@ -36087,15 +36065,6 @@ const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, sc
36087
36065
  return false;
36088
36066
  };
36089
36067
  const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
36090
- const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
36091
- "clear",
36092
- "delete",
36093
- "entries",
36094
- "get",
36095
- "has",
36096
- "keys",
36097
- "values"
36098
- ]);
36099
36068
  const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
36100
36069
  if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
36101
36070
  if (isNodeOfType(returnedValue, "CallExpression")) {
@@ -36146,135 +36115,27 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
36146
36115
  });
36147
36116
  return didFindOpaqueSetterCall;
36148
36117
  };
36149
- const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
36150
- allowGlobalReactNamespace: true,
36151
- allowUnboundBareCalls: true,
36152
- resolveNamedAliases: true
36153
- }) || isReactApiCall(expression, "createRef", scopes, {
36154
- allowGlobalReactNamespace: true,
36155
- allowUnboundBareCalls: true,
36156
- resolveNamedAliases: true
36157
- }));
36158
- const getDirectReactRefSymbol = (rawExpression, scopes) => {
36159
- const expression = stripParenExpression(rawExpression);
36160
- if (!isNodeOfType(expression, "Identifier")) return null;
36161
- const symbol = scopes.symbolFor(expression);
36162
- if (!symbol) return null;
36163
- const initializer = getDirectUnreassignedInitializer(symbol);
36164
- return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
36165
- };
36166
- const isReactNativeJsxElement = (openingElement, scopes) => {
36167
- if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
36168
- const symbol = scopes.symbolFor(openingElement.name);
36169
- const importDeclaration = symbol?.declarationNode.parent;
36170
- return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
36171
- };
36172
- const isDirectHostJsxRef = (symbol, scopes) => {
36173
- let hostRefCount = 0;
36174
- for (const reference of symbol.references) {
36175
- const expression = findTransparentExpressionRoot(reference.identifier);
36176
- const container = expression.parent;
36177
- if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
36178
- if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
36179
- const attribute = container.parent;
36180
- if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
36181
- const openingElement = attribute.parent;
36182
- if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
36183
- hostRefCount += 1;
36184
- }
36185
- return hostRefCount > 0;
36186
- };
36187
- const isIntrinsicRefCallbackParameter = (expression, scopes) => {
36188
- const identifier = stripParenExpression(expression);
36189
- if (!isNodeOfType(identifier, "Identifier")) return false;
36190
- const callback = findEnclosingFunction$1(identifier);
36191
- if (!callback || !isFunctionLike$1(callback) || !isInlineIntrinsicRefCallback(callback, scopes)) return false;
36192
- const rawFirstParameter = callback.params?.[0];
36193
- const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
36194
- const symbol = scopes.symbolFor(identifier);
36195
- return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
36196
- };
36197
- const getDirectReactRefCall = (symbol, scopes) => {
36198
- const initializer = getDirectUnreassignedInitializer(symbol);
36199
- if (!initializer) return null;
36200
- const expression = stripParenExpression(initializer);
36201
- return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
36202
- };
36203
- const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
36204
- const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
36205
- if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
36206
- let intrinsicValueWriteCount = 0;
36207
- for (const reference of symbol.references) {
36208
- const identifier = findTransparentExpressionRoot(reference.identifier);
36209
- const currentMember = identifier.parent;
36210
- if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
36211
- const currentExpression = findTransparentExpressionRoot(currentMember);
36212
- const methodMember = currentExpression.parent;
36213
- if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
36214
- const methodName = getStaticPropertyName(methodMember);
36215
- if (methodName === "size") continue;
36216
- const call = methodMember.parent;
36217
- if (!isNodeOfType(call, "CallExpression") || call.callee !== methodMember) return false;
36218
- if (methodName && NON_CONTAMINATING_MAP_METHOD_NAMES.has(methodName)) continue;
36219
- if (methodName !== "set") return false;
36220
- const storedValue = call.arguments[1];
36221
- if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
36222
- intrinsicValueWriteCount += 1;
36223
- }
36224
- return intrinsicValueWriteCount > 0;
36225
- };
36226
- const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36227
- const expression = stripParenExpression(rawExpression);
36228
- if (isNodeOfType(expression, "Identifier")) {
36229
- const symbol = scopes.symbolFor(expression);
36230
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
36231
- const initializer = getDirectUnreassignedInitializer(symbol);
36232
- if (!initializer) return false;
36233
- visitedSymbolIds.add(symbol.id);
36234
- return isDerivedFromProvenDomRefCurrent(initializer, scopes, didReadCollectionValue, visitedSymbolIds);
36235
- }
36236
- if (isNodeOfType(expression, "MemberExpression")) {
36237
- if (getStaticPropertyName(expression) === "current") {
36238
- const symbol = getDirectReactRefSymbol(expression.object, scopes);
36239
- return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
36240
- }
36241
- return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
36242
- }
36243
- if (!isNodeOfType(expression, "CallExpression")) return false;
36244
- const callee = stripParenExpression(expression.callee);
36245
- if (!isNodeOfType(callee, "MemberExpression")) return false;
36246
- return isDerivedFromProvenDomRefCurrent(callee.object, scopes, didReadCollectionValue || getStaticPropertyName(callee) === "get", visitedSymbolIds);
36247
- };
36248
- const isCommittedDomSyncNode = (node, scopes) => {
36249
- if (!isNodeOfType(node, "CallExpression")) return false;
36250
- const callee = stripParenExpression(node.callee);
36251
- if (!isNodeOfType(callee, "MemberExpression")) return false;
36252
- const propertyName = getStaticPropertyName(callee);
36253
- if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
36254
- return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
36255
- };
36256
36118
  const isExternalSyncNode = (node) => {
36257
36119
  if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
36258
36120
  if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
36259
36121
  if (!isNodeOfType(node, "CallExpression")) return false;
36260
36122
  if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
36261
- if (!isNodeOfType(node.callee, "MemberExpression")) return false;
36262
- const propertyName = getStaticPropertyName(node.callee);
36263
- if (propertyName === null) return false;
36123
+ if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier")) return false;
36124
+ const propertyName = node.callee.property.name;
36264
36125
  if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
36265
36126
  if (isBrowserStorageReceiver(node.callee.object)) return true;
36266
36127
  if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
36267
36128
  const receiverRootName = getRootIdentifierName(node.callee.object);
36268
36129
  return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
36269
36130
  };
36270
- const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, scopes, allowCommittedDomSync) => {
36131
+ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName) => {
36271
36132
  if (!isFunctionLike$1(effectCallback)) return false;
36272
36133
  if (!isNodeOfType(effectCallback.body, "BlockStatement")) {
36273
36134
  if (isFunctionShapedReturn(effectCallback.body, setterToStateName, false)) return true;
36274
36135
  } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, true)) return true;
36275
36136
  let didFindExternalCall = false;
36276
36137
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
36277
- if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
36138
+ if (isExternalSyncNode(child)) didFindExternalCall = true;
36278
36139
  });
36279
36140
  return didFindExternalCall;
36280
36141
  };
@@ -36313,7 +36174,7 @@ const noEffectChain = defineRule({
36313
36174
  depNames: collectDepIdentifierNames(effectCall),
36314
36175
  stateWrites,
36315
36176
  analysisFunctions,
36316
- isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName, context.scopes, writtenStateNames.size === 0) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
36177
+ isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
36317
36178
  });
36318
36179
  }
36319
36180
  if (effectInfos.length < 2) return;
@@ -39995,18 +39856,6 @@ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
39995
39856
  return containsReactHookCall;
39996
39857
  };
39997
39858
  //#endregion
39998
- //#region src/plugin/utils/function-returns-only-null.ts
39999
- const isNullExpression = (expression) => {
40000
- const candidate = stripParenExpression(expression);
40001
- return isNodeOfType(candidate, "Literal") && candidate.value === null;
40002
- };
40003
- const functionReturnsOnlyNull = (functionNode) => {
40004
- if (!isFunctionLike$1(functionNode)) return false;
40005
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
40006
- const returnStatements = collectFunctionReturnStatements(functionNode);
40007
- return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
40008
- };
40009
- //#endregion
40010
39859
  //#region src/plugin/utils/function-returns-props-children.ts
40011
39860
  const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
40012
39861
  if (!isFunctionLike$1(functionNode) || functionNode.params.length === 0) return false;
@@ -40039,8 +39888,17 @@ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
40039
39888
  }, controlFlow);
40040
39889
  };
40041
39890
  //#endregion
40042
- //#region src/plugin/utils/function-has-react-component-evidence.ts
40043
- const functionHasReactComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39891
+ //#region src/plugin/utils/function-returns-only-null.ts
39892
+ const isNullExpression = (expression) => {
39893
+ const candidate = stripParenExpression(expression);
39894
+ return isNodeOfType(candidate, "Literal") && candidate.value === null;
39895
+ };
39896
+ const functionReturnsOnlyNull = (functionNode) => {
39897
+ if (!isFunctionLike$1(functionNode)) return false;
39898
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
39899
+ const returnStatements = collectFunctionReturnStatements(functionNode);
39900
+ return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
39901
+ };
40044
39902
  //#endregion
40045
39903
  //#region src/plugin/utils/is-proven-styled-component-expression.ts
40046
39904
  const findFactoryRoot = (node) => {
@@ -40073,16 +39931,17 @@ const isProvenStyledComponentExpression = (expression, scopes) => {
40073
39931
  //#region src/plugin/utils/is-proven-react-component-symbol.ts
40074
39932
  const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
40075
39933
  const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
39934
+ const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
40076
39935
  const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40077
39936
  const candidate = stripParenExpression(expression);
40078
- if (isInlineFunctionExpression(candidate)) return functionHasReactComponentEvidence(candidate, scopes, controlFlow);
39937
+ if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
40079
39938
  if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
40080
39939
  if (isProvenStyledComponentExpression(candidate, scopes)) return true;
40081
39940
  if (isNodeOfType(candidate, "Identifier")) {
40082
39941
  const symbol = scopes.symbolFor(candidate);
40083
39942
  if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
40084
39943
  visitedSymbolIds.add(symbol.id);
40085
- if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasReactComponentEvidence(symbol.declarationNode, scopes, controlFlow);
39944
+ if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
40086
39945
  if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
40087
39946
  return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
40088
39947
  }
@@ -40109,7 +39968,7 @@ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentRefe
40109
39968
  for (const candidateSymbol of candidateSymbols) {
40110
39969
  if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
40111
39970
  if (isComponentDeclaration(candidateSymbol.declarationNode)) {
40112
- if (functionHasReactComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
39971
+ if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
40113
39972
  continue;
40114
39973
  }
40115
39974
  const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
@@ -43895,53 +43754,6 @@ const noPropCallbackInEffect = defineRule({
43895
43754
  });
43896
43755
  //#endregion
43897
43756
  //#region src/plugin/rules/state-and-effects/no-prop-callback-in-render.ts
43898
- const functionBindingSymbols = (functionNode, scopes) => {
43899
- let bindingIdentifier = null;
43900
- if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) bindingIdentifier = functionNode.id;
43901
- else {
43902
- let bindingExpression = findTransparentExpressionRoot(functionNode);
43903
- let parent = bindingExpression.parent;
43904
- while (isNodeOfType(parent, "CallExpression") && parent.arguments[0] === bindingExpression) {
43905
- const callee = parent.callee;
43906
- const wrapperName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") ? callee.property.name : null;
43907
- if (!isReactApiCall(parent, REACT_HOC_NAMES, scopes, {
43908
- allowGlobalReactNamespace: true,
43909
- resolveNamedAliases: true
43910
- }) && (!wrapperName || REACT_HOC_NAMES.has(wrapperName) || !COMPONENT_HOC_WRAPPER_NAMES.has(wrapperName))) break;
43911
- bindingExpression = findTransparentExpressionRoot(parent);
43912
- parent = bindingExpression.parent;
43913
- }
43914
- if (isNodeOfType(parent, "VariableDeclarator") && parent.init === bindingExpression && isNodeOfType(parent.id, "Identifier")) bindingIdentifier = parent.id;
43915
- }
43916
- if (!bindingIdentifier) return [];
43917
- let scope = scopes.scopeFor(functionNode);
43918
- while (scope) {
43919
- const symbols = scope.symbols.filter((symbol) => symbol.bindingIdentifier === bindingIdentifier);
43920
- if (symbols.length > 0) return symbols;
43921
- scope = scope.parent;
43922
- }
43923
- return [];
43924
- };
43925
- const symbolHasReactComponentUse = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
43926
- if (visitedSymbolIds.has(symbol.id)) return false;
43927
- visitedSymbolIds.add(symbol.id);
43928
- for (const reference of symbol.references) {
43929
- const identifier = reference.identifier;
43930
- if (hasSymbolWriteBefore(symbol, identifier, scopes)) continue;
43931
- const parent = identifier.parent;
43932
- if (isNodeOfType(parent, "JSXOpeningElement") && isNodeOfType(parent.name, "JSXIdentifier") && parent.name === identifier) return true;
43933
- const expression = findTransparentExpressionRoot(identifier);
43934
- const expressionParent = expression.parent;
43935
- if (isNodeOfType(expressionParent, "CallExpression") && expressionParent.arguments[0] === expression && isReactApiCall(expressionParent, "createElement", scopes, { resolveNamedAliases: true })) return true;
43936
- if (!isNodeOfType(expressionParent, "VariableDeclarator") || expressionParent.init !== expression || !isNodeOfType(expressionParent.id, "Identifier") || !isNodeOfType(expressionParent.parent, "VariableDeclaration") || expressionParent.parent.kind !== "const") continue;
43937
- const aliasSymbol = scopes.symbolFor(expressionParent.id);
43938
- if (aliasSymbol && symbolHasReactComponentUse(aliasSymbol, scopes, visitedSymbolIds)) return true;
43939
- }
43940
- return false;
43941
- };
43942
- const functionHasReactComponentUse = (functionNode, scopes) => {
43943
- return functionBindingSymbols(functionNode, scopes).some((symbol) => symbolHasReactComponentUse(symbol, scopes));
43944
- };
43945
43757
  const isPreservedThroughConciseArrow = (callExpression, scopes) => {
43946
43758
  let node = callExpression;
43947
43759
  let parent = node.parent;
@@ -43988,10 +43800,7 @@ const noPropCallbackInRender = defineRule({
43988
43800
  create: (context) => ({ CallExpression(node) {
43989
43801
  if (!isResultDiscardedCall(node)) return;
43990
43802
  if (isPreservedThroughConciseArrow(node, context.scopes)) return;
43991
- const renderPhaseOwner = findRenderPhaseComponentOrHook(node, context.scopes);
43992
- if (!renderPhaseOwner) return;
43993
- const renderPhaseOwnerName = componentOrHookDisplayNameForFunction(renderPhaseOwner);
43994
- if (!renderPhaseOwnerName || !isReactHookName(renderPhaseOwnerName) && !functionHasReactComponentEvidence(renderPhaseOwner, context.scopes, context.cfg) && !functionHasReactComponentUse(renderPhaseOwner, context.scopes)) return;
43803
+ if (!findRenderPhaseComponentOrHook(node, context.scopes)) return;
43995
43804
  const analysis = getProgramAnalysis(node);
43996
43805
  if (!analysis) return;
43997
43806
  const callee = stripParenExpression(node.callee);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.7.9-dev.c7f61cd",
3
+ "version": "0.7.9-dev.c88a39a",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",