oxlint-plugin-react-doctor 0.7.8-dev.ece35b3 → 0.7.9-dev.194cc06

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 +1045 -49
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11674,7 +11674,7 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
11674
11674
  };
11675
11675
  //#endregion
11676
11676
  //#region src/plugin/utils/get-function-binding-name.ts
11677
- const getFunctionBindingIdentifier = (functionNode) => {
11677
+ const getFunctionBindingIdentifier$1 = (functionNode) => {
11678
11678
  if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
11679
11679
  const parent = functionNode.parent;
11680
11680
  if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) return parent.id;
@@ -11685,7 +11685,7 @@ const getFunctionBindingIdentifier = (functionNode) => {
11685
11685
  }
11686
11686
  return null;
11687
11687
  };
11688
- const getFunctionBindingName$1 = (functionNode) => getFunctionBindingIdentifier(functionNode)?.name ?? null;
11688
+ const getFunctionBindingName$1 = (functionNode) => getFunctionBindingIdentifier$1(functionNode)?.name ?? null;
11689
11689
  //#endregion
11690
11690
  //#region src/plugin/utils/is-event-handler-attribute.ts
11691
11691
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && /^on[A-Z]/.test(node.name.name);
@@ -12272,7 +12272,7 @@ const findDirectCallForReference = (identifier) => {
12272
12272
  return isNodeOfType(callNode, "CallExpression") && callNode.callee === expressionRoot ? callNode : null;
12273
12273
  };
12274
12274
  const findSingleDirectInvocation = (functionNode, caller, context) => {
12275
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
12275
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
12276
12276
  if (!bindingIdentifier || resolveStableValue(bindingIdentifier, context) !== functionNode) return null;
12277
12277
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12278
12278
  if (!symbol) return null;
@@ -12809,7 +12809,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
12809
12809
  };
12810
12810
  const isPotentiallyReachableFunction = (functionNode, context) => {
12811
12811
  if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode)) return true;
12812
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
12812
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
12813
12813
  if (!bindingIdentifier) return false;
12814
12814
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12815
12815
  if (!symbol) return false;
@@ -13043,7 +13043,7 @@ const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) =
13043
13043
  return didFindCleanupEffect;
13044
13044
  };
13045
13045
  const isUseSyncExternalStoreSubscribeFunction = (functionNode, context) => {
13046
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
13046
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
13047
13047
  if (!bindingIdentifier) return false;
13048
13048
  const visitedSymbolIds = /* @__PURE__ */ new Set();
13049
13049
  const isSubscribeBinding = (candidateBinding) => {
@@ -21492,7 +21492,7 @@ const namedCallbackIteratorCallCache = /* @__PURE__ */ new WeakMap();
21492
21492
  const findNamedCallbackIteratorCall = (functionNode) => {
21493
21493
  const cached = namedCallbackIteratorCallCache.get(functionNode);
21494
21494
  if (cached !== void 0) return cached;
21495
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
21495
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
21496
21496
  if (!bindingIdentifier) {
21497
21497
  namedCallbackIteratorCallCache.set(functionNode, null);
21498
21498
  return null;
@@ -22317,7 +22317,7 @@ const isReactSlotType = (typeNode, environment, activeDeclarations) => {
22317
22317
  }
22318
22318
  return environment.reactSlotTypeBindings.has(typeName);
22319
22319
  };
22320
- const getPropertyName = (property) => {
22320
+ const getPropertyName$1 = (property) => {
22321
22321
  if (!isNodeOfType(property, "TSPropertySignature") || property.computed) return null;
22322
22322
  if (isNodeOfType(property.key, "Identifier")) return property.key.name;
22323
22323
  if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
@@ -22329,7 +22329,7 @@ const collectSlotPropertyNames = (propsType, environment, slotPropertyNames, act
22329
22329
  if (isNodeOfType(propsType, "TSInterfaceDeclaration")) members = propsType.body.body;
22330
22330
  if (members) {
22331
22331
  for (const member of members) {
22332
- const propertyName = getPropertyName(member);
22332
+ const propertyName = getPropertyName$1(member);
22333
22333
  if (!propertyName || !isNodeOfType(member, "TSPropertySignature")) continue;
22334
22334
  const annotation = member.typeAnnotation;
22335
22335
  if (annotation && isNodeOfType(annotation, "TSTypeAnnotation") && isReactSlotType(annotation.typeAnnotation, environment, /* @__PURE__ */ new Set())) slotPropertyNames.add(propertyName);
@@ -31988,17 +31988,611 @@ const noCreateContextInRender = defineRule({
31988
31988
  } })
31989
31989
  });
31990
31990
  //#endregion
31991
+ //#region src/plugin/utils/collect-function-children-references.ts
31992
+ const collectFunctionChildrenReferences = (functionNode, scopes) => {
31993
+ if (!isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "FunctionDeclaration")) return null;
31994
+ const rawPropsParameter = functionNode.params[0];
31995
+ const propsParameter = isNodeOfType(rawPropsParameter, "AssignmentPattern") ? rawPropsParameter.left : rawPropsParameter;
31996
+ if (isNodeOfType(propsParameter, "ObjectPattern")) {
31997
+ const childrenProperty = propsParameter.properties.find((property) => isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children");
31998
+ if (!childrenProperty || !isNodeOfType(childrenProperty, "Property")) return null;
31999
+ const childrenBinding = isNodeOfType(childrenProperty.value, "AssignmentPattern") ? childrenProperty.value.left : childrenProperty.value;
32000
+ if (!isNodeOfType(childrenBinding, "Identifier")) return null;
32001
+ const childrenSymbol = scopes.symbolFor(childrenBinding);
32002
+ if (!childrenSymbol || childrenSymbol.references.length === 0 || childrenSymbol.references.some((reference) => reference.flag !== "read")) return null;
32003
+ return childrenSymbol.references.map((reference) => reference.identifier);
32004
+ }
32005
+ if (!isNodeOfType(propsParameter, "Identifier")) return null;
32006
+ const propsSymbol = scopes.symbolFor(propsParameter);
32007
+ if (!propsSymbol || propsSymbol.references.length === 0) return null;
32008
+ const childrenReferences = [];
32009
+ for (const reference of propsSymbol.references) {
32010
+ if (reference.flag !== "read") return null;
32011
+ const propertyPath = [];
32012
+ let expression = findTransparentExpressionRoot(reference.identifier);
32013
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32014
+ const propertyName = getStaticPropertyName(expression.parent);
32015
+ if (!propertyName) return null;
32016
+ propertyPath.push(propertyName);
32017
+ expression = findTransparentExpressionRoot(expression.parent);
32018
+ }
32019
+ if (propertyPath[0] !== "children") {
32020
+ if (propertyPath.length === 0) return null;
32021
+ continue;
32022
+ }
32023
+ if (propertyPath.length !== 1) return null;
32024
+ childrenReferences.push(expression);
32025
+ }
32026
+ return childrenReferences.length > 0 ? childrenReferences : null;
32027
+ };
32028
+ //#endregion
32029
+ //#region src/plugin/utils/is-proven-react-class-component.ts
32030
+ const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
32031
+ const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
32032
+ const expression = stripParenExpression(node);
32033
+ if (isNodeOfType(expression, "MemberExpression")) {
32034
+ const propertyName = getStaticPropertyName(expression);
32035
+ const receiver = stripParenExpression(expression.object);
32036
+ return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
32037
+ }
32038
+ if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
32039
+ if (!isNodeOfType(expression, "Identifier")) return false;
32040
+ const symbol = scopes.symbolFor(expression);
32041
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
32042
+ visitedSymbolIds.add(symbol.id);
32043
+ if (isImportedFromReact(symbol)) {
32044
+ const importedName = getImportedName(symbol.declarationNode);
32045
+ return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
32046
+ }
32047
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
32048
+ return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
32049
+ };
32050
+ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32051
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
32052
+ visitedClassNodes.add(classNode);
32053
+ return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
32054
+ };
32055
+ //#endregion
32056
+ //#region src/plugin/utils/is-proven-intrinsic-jsx-element.ts
32057
+ const isProvenIntrinsicJsxElement = (openingElement, scopes) => {
32058
+ if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
32059
+ const resolvedElementType = resolveJsxElementType(openingElement);
32060
+ if (resolvedElementType[0] === resolvedElementType[0]?.toLowerCase()) return true;
32061
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
32062
+ const isIntrinsicValue = (node) => {
32063
+ const current = findTransparentExpressionRoot(node);
32064
+ if (isNodeOfType(current, "Literal")) return typeof current.value === "string";
32065
+ if (isNodeOfType(current, "Identifier") || isNodeOfType(current, "JSXIdentifier")) {
32066
+ const symbol = scopes.symbolFor(current);
32067
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return false;
32068
+ visitedSymbolIds.add(symbol.id);
32069
+ const isIntrinsic = isIntrinsicValue(symbol.initializer);
32070
+ visitedSymbolIds.delete(symbol.id);
32071
+ return isIntrinsic;
32072
+ }
32073
+ if (isNodeOfType(current, "ConditionalExpression")) return isIntrinsicValue(current.consequent) && isIntrinsicValue(current.alternate);
32074
+ return false;
32075
+ };
32076
+ return isIntrinsicValue(openingElement.name);
32077
+ };
32078
+ //#endregion
32079
+ //#region src/plugin/rules/react-builtins/is-safe-create-ref-callback-current-write.ts
32080
+ const pathStartsWith$1 = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32081
+ const collectMemberExpression = (identifier) => {
32082
+ let expression = findTransparentExpressionRoot(identifier);
32083
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32084
+ if (!getStaticPropertyName(expression.parent)) return null;
32085
+ expression = findTransparentExpressionRoot(expression.parent);
32086
+ }
32087
+ return expression;
32088
+ };
32089
+ const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
32090
+ const functionExpression = findTransparentExpressionRoot(functionNode);
32091
+ if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
32092
+ const container = functionExpression.parent;
32093
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer")) return false;
32094
+ const attribute = container.parent;
32095
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
32096
+ const openingElement = attribute.parent;
32097
+ return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
32098
+ };
32099
+ const isSafeCreateRefCallbackCurrentWrite = (referenceNode, accessedPropertyPath, targetPropertyPath, scopes) => {
32100
+ if (accessedPropertyPath.length !== targetPropertyPath.length + 1 || !pathStartsWith$1(accessedPropertyPath, targetPropertyPath) || accessedPropertyPath[targetPropertyPath.length] !== "current") return false;
32101
+ const memberExpression = collectMemberExpression(referenceNode);
32102
+ const assignment = memberExpression?.parent;
32103
+ if (!memberExpression || !assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.left !== memberExpression) return false;
32104
+ const enclosingFunction = findEnclosingFunction$1(referenceNode);
32105
+ if (!enclosingFunction) return false;
32106
+ if (isInlineIntrinsicRefCallback(enclosingFunction, scopes)) return true;
32107
+ if (!isFunctionLike$1(enclosingFunction) || enclosingFunction.async || enclosingFunction.generator) return false;
32108
+ const callbackFunction = findEnclosingFunction$1(enclosingFunction);
32109
+ if (!callbackFunction || !isInlineIntrinsicRefCallback(callbackFunction, scopes)) return false;
32110
+ const cleanupFunction = findTransparentExpressionRoot(enclosingFunction);
32111
+ const cleanupContainer = cleanupFunction.parent;
32112
+ return Boolean(isNodeOfType(cleanupContainer, "ReturnStatement") && cleanupContainer.argument === cleanupFunction && findEnclosingFunction$1(cleanupContainer) === callbackFunction || isNodeOfType(callbackFunction, "ArrowFunctionExpression") && callbackFunction.body === cleanupFunction);
32113
+ };
32114
+ //#endregion
32115
+ //#region src/plugin/rules/react-builtins/is-value-rendered-in-same-render.ts
32116
+ const isValueRenderedFromOwner = (expressionNode, renderOwner, scopes, visitedSymbolIds, options) => {
32117
+ const expression = findTransparentExpressionRoot(expressionNode);
32118
+ const parent = expression.parent;
32119
+ if (!parent) return false;
32120
+ if (isNodeOfType(parent, "ReturnStatement") && parent.argument === expression) return findEnclosingFunction$1(parent) === renderOwner;
32121
+ if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === expression) return parent === renderOwner;
32122
+ if (isNodeOfType(parent, "JSXFragment") && parent.children.some((child) => child === expression)) return isValueRenderedFromOwner(parent, renderOwner, scopes, visitedSymbolIds, options);
32123
+ if (isNodeOfType(parent, "JSXElement") && parent.children.some((child) => child === expression) && (isProvenIntrinsicJsxElement(parent.openingElement, scopes) || isJsxFragmentElement(parent.openingElement, scopes) || options.doesCustomElementRenderChildren?.(parent.openingElement))) return isValueRenderedFromOwner(parent, renderOwner, scopes, visitedSymbolIds, options);
32124
+ if (isNodeOfType(parent, "JSXExpressionContainer") && parent.expression === expression && parent.parent && (isNodeOfType(parent.parent, "JSXFragment") || isNodeOfType(parent.parent, "JSXElement") && (isProvenIntrinsicJsxElement(parent.parent.openingElement, scopes) || isJsxFragmentElement(parent.parent.openingElement, scopes) || options.doesCustomElementRenderChildren?.(parent.parent.openingElement)))) return isValueRenderedFromOwner(parent.parent, renderOwner, scopes, visitedSymbolIds, options);
32125
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === expression || parent.alternate === expression) || isNodeOfType(parent, "LogicalExpression") && (parent.left === expression || parent.right === expression) || isNodeOfType(parent, "ArrayExpression") && parent.elements.some((element) => element === expression)) return isValueRenderedFromOwner(parent, renderOwner, scopes, visitedSymbolIds, options);
32126
+ if (!isNodeOfType(parent, "VariableDeclarator") || parent.init !== expression || !isNodeOfType(parent.id, "Identifier")) return false;
32127
+ const symbol = scopes.symbolFor(parent.id);
32128
+ if (!symbol || symbol.kind !== "const" || symbol.references.length === 0 || visitedSymbolIds.has(symbol.id)) return false;
32129
+ visitedSymbolIds.add(symbol.id);
32130
+ const isRendered = symbol.references.every((reference) => reference.flag === "read" && isValueRenderedFromOwner(reference.identifier, renderOwner, scopes, visitedSymbolIds, options));
32131
+ visitedSymbolIds.delete(symbol.id);
32132
+ return isRendered;
32133
+ };
32134
+ const isValueRenderedInSameRender = (expressionNode, scopes, options = {}) => isValueRenderedFromOwner(expressionNode, findEnclosingFunction$1(expressionNode), scopes, /* @__PURE__ */ new Set(), options);
32135
+ //#endregion
32136
+ //#region src/plugin/rules/react-builtins/is-create-ref-result-write-only.ts
32137
+ const pathStartsWith = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32138
+ const pathsOverlap = (firstPath, secondPath) => pathStartsWith(firstPath, secondPath) || pathStartsWith(secondPath, firstPath);
32139
+ const isClosedNoopFunction = (node) => isFunctionLike$1(node) && isNodeOfType(node.body, "BlockStatement") && node.body.body.length === 0;
32140
+ const isProvenReactCall = (callExpression, expectedName, scopes) => isReactApiCall(callExpression, expectedName, scopes, { resolveNamedAliases: true });
32141
+ const objectHasOnlyClosedNoopFunctions = (objectExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32142
+ if (!isNodeOfType(objectExpression, "ObjectExpression")) return false;
32143
+ return objectExpression.properties.every((property) => {
32144
+ if (isNodeOfType(property, "SpreadElement")) {
32145
+ const spreadArgument = findTransparentExpressionRoot(property.argument);
32146
+ if (isNodeOfType(spreadArgument, "ObjectExpression")) return objectHasOnlyClosedNoopFunctions(spreadArgument, scopes, visitedSymbolIds);
32147
+ if (!isNodeOfType(spreadArgument, "Identifier")) return false;
32148
+ const symbol = scopes.symbolFor(spreadArgument);
32149
+ if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.initializer, "ObjectExpression")) return false;
32150
+ visitedSymbolIds.add(symbol.id);
32151
+ const isClosed = objectHasOnlyClosedNoopFunctions(symbol.initializer, scopes, visitedSymbolIds);
32152
+ visitedSymbolIds.delete(symbol.id);
32153
+ return isClosed;
32154
+ }
32155
+ if (!isNodeOfType(property, "Property")) return false;
32156
+ return !isFunctionLike$1(property.value) || isClosedNoopFunction(property.value);
32157
+ });
32158
+ };
32159
+ const getValuePathPropertyName = (memberExpression) => {
32160
+ const propertyName = getStaticPropertyName(memberExpression);
32161
+ if (propertyName) return propertyName;
32162
+ return memberExpression.computed && isNodeOfType(memberExpression.property, "Literal") && typeof memberExpression.property.value === "number" && Number.isSafeInteger(memberExpression.property.value) && memberExpression.property.value >= 0 ? String(memberExpression.property.value) : null;
32163
+ };
32164
+ const collectMemberAccess = (identifier) => {
32165
+ const propertyPath = [];
32166
+ let expression = findTransparentExpressionRoot(identifier);
32167
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32168
+ const propertyName = getValuePathPropertyName(expression.parent);
32169
+ if (!propertyName) return null;
32170
+ propertyPath.push(propertyName);
32171
+ expression = findTransparentExpressionRoot(expression.parent);
32172
+ }
32173
+ return {
32174
+ expression,
32175
+ propertyPath
32176
+ };
32177
+ };
32178
+ const getEnvironment = (program, filename, state) => {
32179
+ const cached = state.environmentsByProgram.get(program);
32180
+ if (cached) return cached;
32181
+ const environment = {
32182
+ filename,
32183
+ program,
32184
+ scopes: analyzeScopes(program)
32185
+ };
32186
+ state.environmentsByProgram.set(program, environment);
32187
+ return environment;
32188
+ };
32189
+ const unwrapProvenReactHocFunction = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32190
+ if (!node) return null;
32191
+ const current = findTransparentExpressionRoot(node);
32192
+ if (isFunctionLike$1(current)) return current;
32193
+ if (isNodeOfType(current, "Identifier")) {
32194
+ const symbol = scopes.symbolFor(current);
32195
+ if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer || hasSymbolWriteBefore(symbol, current, scopes)) return null;
32196
+ visitedSymbolIds.add(symbol.id);
32197
+ return unwrapProvenReactHocFunction(symbol.initializer, scopes, visitedSymbolIds);
32198
+ }
32199
+ if (!isNodeOfType(current, "CallExpression")) return null;
32200
+ if (!isProvenReactCall(current, "memo", scopes) && !isProvenReactCall(current, "forwardRef", scopes)) return null;
32201
+ const firstArgument = current.arguments[0];
32202
+ if (!firstArgument || isNodeOfType(firstArgument, "SpreadElement")) return null;
32203
+ return unwrapProvenReactHocFunction(firstArgument, scopes, visitedSymbolIds);
32204
+ };
32205
+ const isForwardRefValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32206
+ const current = findTransparentExpressionRoot(node);
32207
+ if (isNodeOfType(current, "Identifier")) {
32208
+ const symbol = scopes.symbolFor(current);
32209
+ if (!symbol || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, current, scopes)) return false;
32210
+ visitedSymbolIds.add(symbol.id);
32211
+ return isForwardRefValue(symbol.initializer, scopes, visitedSymbolIds);
32212
+ }
32213
+ if (!isNodeOfType(current, "CallExpression")) return false;
32214
+ if (isProvenReactCall(current, "forwardRef", scopes)) return true;
32215
+ if (!isProvenReactCall(current, "memo", scopes)) return false;
32216
+ const firstArgument = current.arguments[0];
32217
+ if (!firstArgument || isNodeOfType(firstArgument, "SpreadElement")) return false;
32218
+ return isForwardRefValue(firstArgument, scopes, visitedSymbolIds);
32219
+ };
32220
+ const functionFromExport = (resolved, state) => {
32221
+ const environment = getEnvironment(resolved.programNode, resolved.filePath, state);
32222
+ const functionNode = unwrapProvenReactHocFunction(resolved.exportedNode, environment.scopes);
32223
+ if (!functionNode) return null;
32224
+ return {
32225
+ environment,
32226
+ functionNode,
32227
+ isForwardRef: isForwardRefValue(resolved.exportedNode, environment.scopes)
32228
+ };
32229
+ };
32230
+ const resolveFunctionValue = (identifier, environment, state) => {
32231
+ if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
32232
+ const symbol = environment.scopes.symbolFor(identifier);
32233
+ if (symbol && symbol.kind !== "import") {
32234
+ if (hasSymbolWriteBefore(symbol, identifier, environment.scopes)) return null;
32235
+ const functionNode = unwrapProvenReactHocFunction(symbol.initializer, environment.scopes);
32236
+ if (!functionNode) return null;
32237
+ return {
32238
+ environment,
32239
+ functionNode,
32240
+ isForwardRef: Boolean(symbol.initializer && isForwardRefValue(symbol.initializer, environment.scopes))
32241
+ };
32242
+ }
32243
+ const binding = getImportBindingForName(identifier, identifier.name);
32244
+ if (!binding || binding.isNamespace || binding.exportedName === null) return null;
32245
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, binding.source, binding.exportedName);
32246
+ return resolved ? functionFromExport(resolved, state) : null;
32247
+ };
32248
+ const resolveJsxFunctionValue = (elementName, environment, state) => {
32249
+ if (isNodeOfType(elementName, "JSXIdentifier")) return resolveFunctionValue(elementName, environment, state);
32250
+ if (!isNodeOfType(elementName, "JSXMemberExpression") || !isNodeOfType(elementName.object, "JSXIdentifier")) return null;
32251
+ const namespaceBinding = getImportBindingForName(elementName.object, elementName.object.name);
32252
+ if (!namespaceBinding?.isNamespace) return null;
32253
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, namespaceBinding.source, elementName.property.name);
32254
+ return resolved ? functionFromExport(resolved, state) : null;
32255
+ };
32256
+ const isProvenReactClassValue = (node, environment, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32257
+ const current = findTransparentExpressionRoot(node);
32258
+ if (isNodeOfType(current, "Identifier")) {
32259
+ const symbol = environment.scopes.symbolFor(current);
32260
+ if (!symbol || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, current, environment.scopes)) return false;
32261
+ visitedSymbolIds.add(symbol.id);
32262
+ return isProvenReactClassValue(symbol.initializer, environment, visitedSymbolIds);
32263
+ }
32264
+ return isProvenReactClassComponent(current, environment.scopes);
32265
+ };
32266
+ const isProvenClassComponentIdentifier = (identifier, environment, state) => {
32267
+ if (!isNodeOfType(identifier, "JSXIdentifier")) return false;
32268
+ const symbol = environment.scopes.symbolFor(identifier);
32269
+ if (symbol && symbol.kind !== "import") {
32270
+ if (hasSymbolWriteBefore(symbol, identifier, environment.scopes)) return false;
32271
+ return Boolean(symbol.initializer && isProvenReactClassValue(symbol.initializer, environment));
32272
+ }
32273
+ const binding = getImportBindingForName(identifier, identifier.name);
32274
+ if (!binding || binding.isNamespace || binding.exportedName === null) return false;
32275
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, binding.source, binding.exportedName);
32276
+ if (!resolved) return false;
32277
+ const resolvedEnvironment = getEnvironment(resolved.programNode, resolved.filePath, state);
32278
+ return isProvenReactClassValue(resolved.exportedNode, resolvedEnvironment);
32279
+ };
32280
+ const resolveClassValue = (node, environment) => {
32281
+ if (!node) return null;
32282
+ const classNode = findTransparentExpressionRoot(node);
32283
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || !isProvenReactClassValue(classNode, environment)) return null;
32284
+ return {
32285
+ classNode,
32286
+ environment
32287
+ };
32288
+ };
32289
+ const resolveJsxClassValue = (elementName, environment, state) => {
32290
+ if (!isNodeOfType(elementName, "JSXIdentifier")) return null;
32291
+ const symbol = environment.scopes.symbolFor(elementName);
32292
+ if (symbol && symbol.kind !== "import") {
32293
+ if (hasSymbolWriteBefore(symbol, elementName, environment.scopes)) return null;
32294
+ return resolveClassValue(symbol.initializer, environment);
32295
+ }
32296
+ const binding = getImportBindingForName(elementName, elementName.name);
32297
+ if (!binding || binding.isNamespace || binding.exportedName === null) return null;
32298
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, binding.source, binding.exportedName);
32299
+ if (!resolved) return null;
32300
+ const resolvedEnvironment = getEnvironment(resolved.programNode, resolved.filePath, state);
32301
+ return resolveClassValue(resolved.exportedNode, resolvedEnvironment);
32302
+ };
32303
+ const findOwnedSymbolValue = (expressionNode, initialPropertyPath, environment) => {
32304
+ const propertyPath = [...initialPropertyPath];
32305
+ let expression = findTransparentExpressionRoot(expressionNode);
32306
+ while (expression.parent) {
32307
+ const parent = expression.parent;
32308
+ if (isNodeOfType(parent, "ArrayExpression")) {
32309
+ if (parent.elements.some((element) => element && isNodeOfType(element, "SpreadElement"))) return null;
32310
+ const elementIndex = parent.elements.findIndex((element) => element === expression);
32311
+ if (elementIndex < 0) return null;
32312
+ propertyPath.unshift(String(elementIndex));
32313
+ expression = findTransparentExpressionRoot(parent);
32314
+ continue;
32315
+ }
32316
+ if (isNodeOfType(parent, "Property") && parent.value === expression) {
32317
+ const propertyName = getStaticPropertyKeyName(parent, { allowComputedString: true });
32318
+ const objectExpression = parent.parent;
32319
+ if (!propertyName || !objectExpression || !objectHasOnlyClosedNoopFunctions(objectExpression, environment.scopes)) return null;
32320
+ propertyPath.unshift(propertyName);
32321
+ expression = findTransparentExpressionRoot(objectExpression);
32322
+ continue;
32323
+ }
32324
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === expression || parent.alternate === expression) || isNodeOfType(parent, "LogicalExpression") && (parent.left === expression || parent.right === expression)) {
32325
+ expression = findTransparentExpressionRoot(parent);
32326
+ continue;
32327
+ }
32328
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === expression && isNodeOfType(parent.id, "Identifier")) {
32329
+ const symbol = environment.scopes.symbolFor(parent.id);
32330
+ if (!symbol || symbol.kind !== "const") return null;
32331
+ return {
32332
+ environment,
32333
+ propertyPath,
32334
+ symbol
32335
+ };
32336
+ }
32337
+ if (isNodeOfType(parent, "AssignmentExpression") && parent.operator === "=" && parent.right === expression && isNodeOfType(parent.left, "MemberExpression")) {
32338
+ const assignedPropertyPath = [];
32339
+ let assignedExpression = parent.left;
32340
+ while (isNodeOfType(assignedExpression, "MemberExpression")) {
32341
+ const propertyName = getValuePathPropertyName(assignedExpression);
32342
+ if (!propertyName) return null;
32343
+ assignedPropertyPath.unshift(propertyName);
32344
+ assignedExpression = findTransparentExpressionRoot(assignedExpression.object);
32345
+ }
32346
+ if (!isNodeOfType(assignedExpression, "Identifier")) return null;
32347
+ const symbol = environment.scopes.symbolFor(assignedExpression);
32348
+ if (!symbol || symbol.kind !== "const") return null;
32349
+ return {
32350
+ environment,
32351
+ originWriteReference: assignedExpression,
32352
+ propertyPath: [...assignedPropertyPath, ...propertyPath],
32353
+ symbol
32354
+ };
32355
+ }
32356
+ return null;
32357
+ }
32358
+ return null;
32359
+ };
32360
+ const analyzePatternPath = (pattern, propertyPath, environment, state, remainingDepth) => {
32361
+ if (isNodeOfType(pattern, "AssignmentPattern")) return analyzePatternPath(pattern.left, propertyPath, environment, state, remainingDepth);
32362
+ if (isNodeOfType(pattern, "Identifier")) {
32363
+ const symbol = environment.scopes.symbolFor(pattern);
32364
+ return Boolean(symbol && analyzeSymbolValuePath({
32365
+ environment,
32366
+ propertyPath,
32367
+ symbol
32368
+ }, state, remainingDepth - 1));
32369
+ }
32370
+ if (!isNodeOfType(pattern, "ObjectPattern") || propertyPath.length === 0) return false;
32371
+ const [firstPropertyName, ...remainingPropertyPath] = propertyPath;
32372
+ for (const property of pattern.properties) {
32373
+ if (!isNodeOfType(property, "Property")) continue;
32374
+ const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
32375
+ if (!propertyName) return false;
32376
+ if (propertyName !== firstPropertyName) continue;
32377
+ return analyzePatternPath(property.value, remainingPropertyPath, environment, state, remainingDepth);
32378
+ }
32379
+ const restProperty = pattern.properties.find((property) => isNodeOfType(property, "RestElement"));
32380
+ return restProperty ? analyzePatternPath(restProperty.argument, propertyPath, environment, state, remainingDepth) : true;
32381
+ };
32382
+ const isIntrinsicReactElementFactoryCall = (callExpression, environment) => {
32383
+ const element = callExpression.arguments[0];
32384
+ if (!element || isNodeOfType(element, "SpreadElement")) return false;
32385
+ if (isProvenReactCall(callExpression, "createElement", environment.scopes)) return isNodeOfType(element, "Literal") && typeof element.value === "string";
32386
+ if (!isProvenReactCall(callExpression, "cloneElement", environment.scopes)) return false;
32387
+ if (!isNodeOfType(element, "JSXElement")) return false;
32388
+ return isProvenIntrinsicJsxElement(element.openingElement, environment.scopes);
32389
+ };
32390
+ const isIntrinsicReactElementRefProperty = (expression, propertyPath, environment, state, remainingDepth) => {
32391
+ if (propertyPath.length > 0) return false;
32392
+ const property = expression.parent;
32393
+ if (!property || !isNodeOfType(property, "Property") || property.value !== expression) return false;
32394
+ if (getStaticPropertyKeyName(property, { allowComputedString: true }) !== "ref") return false;
32395
+ const props = property.parent;
32396
+ if (!props || !isNodeOfType(props, "ObjectExpression")) return false;
32397
+ const callExpression = props.parent;
32398
+ if (!callExpression || !isNodeOfType(callExpression, "CallExpression") || callExpression.arguments[1] !== props || !isIntrinsicReactElementFactoryCall(callExpression, environment) || !isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) return false;
32399
+ return true;
32400
+ };
32401
+ const analyzeFunctionInput = (resolvedFunction, parameterIndex, propertyPath, state, remainingDepth) => {
32402
+ if (remainingDepth <= 0 || !isFunctionLike$1(resolvedFunction.functionNode) || resolvedFunction.functionNode.async || resolvedFunction.functionNode.generator) return false;
32403
+ const parameter = resolvedFunction.functionNode.params[parameterIndex];
32404
+ return Boolean(parameter && analyzePatternPath(parameter, propertyPath, resolvedFunction.environment, state, remainingDepth));
32405
+ };
32406
+ const isThisPropsChildren = (node) => {
32407
+ if (!isNodeOfType(node, "MemberExpression") || getStaticPropertyName(node) !== "children") return false;
32408
+ const propsMember = findTransparentExpressionRoot(node.object);
32409
+ return Boolean(isNodeOfType(propsMember, "MemberExpression") && getStaticPropertyName(propsMember) === "props" && isNodeOfType(findTransparentExpressionRoot(propsMember.object), "ThisExpression"));
32410
+ };
32411
+ const isProvenReactContextProvider = (openingElement, environment) => {
32412
+ if (!isNodeOfType(openingElement, "JSXOpeningElement") || !isNodeOfType(openingElement.name, "JSXMemberExpression") || openingElement.name.property.name !== "Provider" || !isNodeOfType(openingElement.name.object, "JSXIdentifier")) return false;
32413
+ const contextSymbol = environment.scopes.symbolFor(openingElement.name.object);
32414
+ return Boolean(contextSymbol?.kind === "const" && contextSymbol.initializer && isNodeOfType(contextSymbol.initializer, "CallExpression") && isProvenReactCall(contextSymbol.initializer, "createContext", environment.scopes));
32415
+ };
32416
+ const isValueRenderedForEnvironment = (expression, environment, state, remainingDepth) => isValueRenderedInSameRender(expression, environment.scopes, { doesCustomElementRenderChildren: (openingElement) => doesCustomElementRenderChildren(openingElement, environment, state, remainingDepth - 1) });
32417
+ const doesFunctionComponentRenderChildren = (resolvedFunction, state, remainingDepth) => {
32418
+ const { functionNode } = resolvedFunction;
32419
+ if (remainingDepth <= 0 || state.activeRenderedChildrenComponents.has(functionNode) || !isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "FunctionDeclaration") || functionNode.async || functionNode.generator) return false;
32420
+ const childrenReferences = collectFunctionChildrenReferences(functionNode, resolvedFunction.environment.scopes);
32421
+ if (!childrenReferences) return false;
32422
+ state.activeRenderedChildrenComponents.add(functionNode);
32423
+ const isRendered = childrenReferences.every((childrenReference) => isValueRenderedForEnvironment(childrenReference, resolvedFunction.environment, state, remainingDepth));
32424
+ state.activeRenderedChildrenComponents.delete(functionNode);
32425
+ return isRendered;
32426
+ };
32427
+ const doesClassComponentRenderChildren = (resolvedClass, state, remainingDepth) => {
32428
+ const { classNode, environment } = resolvedClass;
32429
+ if (remainingDepth <= 0 || state.activeRenderedChildrenComponents.has(classNode) || !isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression")) return false;
32430
+ const renderMethod = classNode.body.body.find((element) => isNodeOfType(element, "MethodDefinition") && !element.computed && isNodeOfType(element.key, "Identifier") && element.key.name === "render");
32431
+ if (!renderMethod || !isNodeOfType(renderMethod, "MethodDefinition")) return false;
32432
+ const childrenReads = [];
32433
+ walkAst(classNode, (node) => {
32434
+ if (isThisPropsChildren(node)) childrenReads.push(node);
32435
+ });
32436
+ if (childrenReads.length === 0) return false;
32437
+ state.activeRenderedChildrenComponents.add(classNode);
32438
+ const isRendered = childrenReads.every((childrenRead) => findEnclosingFunction$1(childrenRead) === renderMethod.value && isValueRenderedForEnvironment(childrenRead, environment, state, remainingDepth));
32439
+ state.activeRenderedChildrenComponents.delete(classNode);
32440
+ return isRendered;
32441
+ };
32442
+ const doesCustomElementRenderChildren = (openingElement, environment, state, remainingDepth) => {
32443
+ if (remainingDepth <= 0 || !isNodeOfType(openingElement, "JSXOpeningElement")) return false;
32444
+ if (isProvenReactContextProvider(openingElement, environment)) return true;
32445
+ const resolvedFunction = resolveJsxFunctionValue(openingElement.name, environment, state);
32446
+ if (resolvedFunction) return doesFunctionComponentRenderChildren(resolvedFunction, state, remainingDepth);
32447
+ const resolvedClass = resolveJsxClassValue(openingElement.name, environment, state);
32448
+ return Boolean(resolvedClass && doesClassComponentRenderChildren(resolvedClass, state, remainingDepth));
32449
+ };
32450
+ const analyzeJsxAttributeUse = (attribute, propertyPath, environment, state, remainingDepth) => {
32451
+ const attributeName = getJsxAttributeName(attribute.name);
32452
+ const openingElement = attribute.parent;
32453
+ if (!attributeName || !openingElement || !isNodeOfType(openingElement, "JSXOpeningElement")) return false;
32454
+ const jsxElement = openingElement.parent;
32455
+ if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement") || !isValueRenderedForEnvironment(jsxElement, environment, state, remainingDepth)) return false;
32456
+ if (isProvenIntrinsicJsxElement(openingElement, environment.scopes)) return attributeName === "ref" && propertyPath.length === 0;
32457
+ if (isNodeOfType(openingElement.name, "JSXIdentifier") && attributeName === "ref" && propertyPath.length === 0 && isProvenClassComponentIdentifier(openingElement.name, environment, state)) return true;
32458
+ const resolvedFunction = resolveJsxFunctionValue(openingElement.name, environment, state);
32459
+ if (!resolvedFunction) return false;
32460
+ if (attributeName === "ref" && resolvedFunction.isForwardRef) return analyzeFunctionInput(resolvedFunction, 1, propertyPath, state, remainingDepth);
32461
+ return analyzeFunctionInput(resolvedFunction, 0, [attributeName, ...propertyPath], state, remainingDepth);
32462
+ };
32463
+ const analyzeCallArgumentUse = (callExpression, argumentExpression, propertyPath, environment, state, remainingDepth) => {
32464
+ const argumentIndex = callExpression.arguments.findIndex((argument) => argument === argumentExpression);
32465
+ if (argumentIndex < 0) return false;
32466
+ if (argumentIndex === 0 && isNodeOfType(callExpression.callee, "Identifier") && callExpression.callee.name === "Boolean" && environment.scopes.isGlobalReference(callExpression.callee)) return true;
32467
+ if (argumentIndex === 0 && isProvenReactCall(callExpression, "useImperativeHandle", environment.scopes)) return propertyPath.length === 0;
32468
+ if (argumentIndex === 1 && propertyPath.length === 1 && propertyPath[0] === "ref" && isIntrinsicReactElementFactoryCall(callExpression, environment) && isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) return true;
32469
+ const inlineCallee = findTransparentExpressionRoot(callExpression.callee);
32470
+ if (isFunctionLike$1(inlineCallee)) {
32471
+ if (functionContainsReactRenderOutput(inlineCallee, environment.scopes) && !isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) return false;
32472
+ return analyzeFunctionInput({
32473
+ environment,
32474
+ functionNode: inlineCallee,
32475
+ isForwardRef: false
32476
+ }, argumentIndex, propertyPath, state, remainingDepth);
32477
+ }
32478
+ if (!isNodeOfType(callExpression.callee, "Identifier")) return false;
32479
+ const resolvedFunction = resolveFunctionValue(callExpression.callee, environment, state);
32480
+ return Boolean(resolvedFunction && (!functionContainsReactRenderOutput(resolvedFunction.functionNode, resolvedFunction.environment.scopes) || isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) && analyzeFunctionInput(resolvedFunction, argumentIndex, propertyPath, state, remainingDepth));
32481
+ };
32482
+ const isIntrinsicJsxSpreadRefUse = (expression, propertyPath, environment, state, remainingDepth) => {
32483
+ let spreadAttribute;
32484
+ if (propertyPath.length === 1 && propertyPath[0] === "ref") spreadAttribute = expression.parent;
32485
+ else if (propertyPath.length === 0) {
32486
+ const property = expression.parent;
32487
+ if (!property || !isNodeOfType(property, "Property") || property.value !== expression || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "ref") return false;
32488
+ spreadAttribute = property.parent?.parent;
32489
+ }
32490
+ if (!spreadAttribute || !isNodeOfType(spreadAttribute, "JSXSpreadAttribute")) return false;
32491
+ const openingElement = spreadAttribute.parent;
32492
+ if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement")) return false;
32493
+ const jsxElement = openingElement.parent;
32494
+ return Boolean(isProvenIntrinsicJsxElement(openingElement, environment.scopes) && jsxElement && isNodeOfType(jsxElement, "JSXElement") && isValueRenderedForEnvironment(jsxElement, environment, state, remainingDepth));
32495
+ };
32496
+ const analyzeValueUse = (expressionNode, propertyPath, environment, state, remainingDepth) => {
32497
+ const expression = findTransparentExpressionRoot(expressionNode);
32498
+ const parent = expression.parent;
32499
+ if (!parent) return false;
32500
+ if (isNodeOfType(parent, "ExpressionStatement")) return true;
32501
+ if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "void" && parent.argument === expression) return true;
32502
+ if (propertyPath.length === 0 && isNodeOfType(parent, "BinaryExpression") && (parent.operator === "===" || parent.operator === "!==" || parent.operator === "==" || parent.operator === "!=")) {
32503
+ const comparedExpression = parent.left === expression ? parent.right : parent.left;
32504
+ return Boolean(isNodeOfType(expression, "Identifier") && isNodeOfType(comparedExpression, "Identifier") && environment.scopes.symbolFor(expression)?.id === environment.scopes.symbolFor(comparedExpression)?.id);
32505
+ }
32506
+ if (isIntrinsicJsxSpreadRefUse(expression, propertyPath, environment, state, remainingDepth)) return true;
32507
+ if (isIntrinsicReactElementRefProperty(expression, propertyPath, environment, state, remainingDepth)) return true;
32508
+ if (propertyPath.length > 0 && (isNodeOfType(parent, "LogicalExpression") && parent.left === expression || isNodeOfType(parent, "ConditionalExpression") && parent.test === expression || isNodeOfType(parent, "IfStatement") && parent.test === expression || isNodeOfType(parent, "UnaryExpression") && parent.operator === "!" && parent.argument === expression)) return true;
32509
+ if (isNodeOfType(parent, "JSXExpressionContainer") && parent.expression === expression) {
32510
+ const attribute = parent.parent;
32511
+ return Boolean(attribute && isNodeOfType(attribute, "JSXAttribute") && analyzeJsxAttributeUse(attribute, propertyPath, environment, state, remainingDepth));
32512
+ }
32513
+ if (isNodeOfType(parent, "CallExpression")) return analyzeCallArgumentUse(parent, expression, propertyPath, environment, state, remainingDepth);
32514
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === expression && !isNodeOfType(parent.id, "Identifier")) return analyzePatternPath(parent.id, propertyPath, environment, state, remainingDepth);
32515
+ const propagatedValue = findOwnedSymbolValue(expression, propertyPath, environment);
32516
+ return Boolean(propagatedValue && analyzeSymbolValuePath(propagatedValue, state, remainingDepth - 1));
32517
+ };
32518
+ const getFunctionBindingIdentifier = (functionNode) => {
32519
+ const parent = functionNode.parent;
32520
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
32521
+ if ((isNodeOfType(functionNode, "FunctionDeclaration") || isNodeOfType(functionNode, "FunctionExpression")) && functionNode.id) return functionNode.id;
32522
+ return null;
32523
+ };
32524
+ const isSynchronouslyInvokedLocalFunction = (functionNode, callerFunction, scopes) => {
32525
+ if (!functionNode || !callerFunction || !isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator) return false;
32526
+ const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
32527
+ if (!bindingIdentifier) return false;
32528
+ if (((isNodeOfType(functionNode, "FunctionDeclaration") || isNodeOfType(functionNode, "FunctionExpression")) && functionNode.id === bindingIdentifier ? findEnclosingFunction$1(functionNode) : findEnclosingFunction$1(bindingIdentifier)) !== callerFunction) return false;
32529
+ const symbol = isNodeOfType(functionNode, "FunctionDeclaration") ? scopes.ownScopeFor(callerFunction)?.symbolsByName.get(bindingIdentifier.name) : scopes.symbolFor(bindingIdentifier);
32530
+ return Boolean(symbol && symbol.references.length > 0 && symbol.references.every((reference) => {
32531
+ const referenceExpression = findTransparentExpressionRoot(reference.identifier);
32532
+ const callExpression = referenceExpression.parent;
32533
+ return Boolean(callExpression && isNodeOfType(callExpression, "CallExpression") && callExpression.callee === referenceExpression && findEnclosingFunction$1(callExpression) === callerFunction && isValueRenderedInSameRender(callExpression, scopes));
32534
+ }));
32535
+ };
32536
+ const analyzeSymbolValuePath = (valuePath, state, remainingDepth) => {
32537
+ if (remainingDepth <= 0) return false;
32538
+ const activePathKey = `${valuePath.environment.filename}:${valuePath.symbol.id}:${valuePath.propertyPath.join(".")}`;
32539
+ if (state.activePaths.has(activePathKey)) return false;
32540
+ state.activePaths.add(activePathKey);
32541
+ const bindingFunction = findEnclosingFunction$1(valuePath.symbol.bindingIdentifier);
32542
+ const result = valuePath.symbol.references.every((reference) => {
32543
+ if (reference.identifier === valuePath.originWriteReference) return true;
32544
+ const referenceParent = reference.identifier.parent;
32545
+ if (referenceParent && isNodeOfType(referenceParent, "Property") && !referenceParent.computed && referenceParent.key === reference.identifier && referenceParent.value !== reference.identifier) return true;
32546
+ const memberAccess = collectMemberAccess(reference.identifier);
32547
+ if (!memberAccess) return false;
32548
+ if (!pathsOverlap(memberAccess.propertyPath, valuePath.propertyPath)) return true;
32549
+ const referenceFunction = findEnclosingFunction$1(reference.identifier);
32550
+ if (referenceFunction !== bindingFunction && !isSynchronouslyInvokedLocalFunction(referenceFunction, bindingFunction, valuePath.environment.scopes)) {
32551
+ if (isSafeCreateRefCallbackCurrentWrite(reference.identifier, memberAccess.propertyPath, valuePath.propertyPath, valuePath.environment.scopes)) return true;
32552
+ return false;
32553
+ }
32554
+ if (reference.flag !== "read") return false;
32555
+ if (pathStartsWith(memberAccess.propertyPath, valuePath.propertyPath)) {
32556
+ if (memberAccess.propertyPath.length > valuePath.propertyPath.length) {
32557
+ const parent = memberAccess.expression.parent;
32558
+ return Boolean(memberAccess.propertyPath.length === valuePath.propertyPath.length + 1 && memberAccess.propertyPath[valuePath.propertyPath.length] === "current" && parent && isNodeOfType(parent, "UnaryExpression") && parent.operator === "void" && parent.argument === memberAccess.expression);
32559
+ }
32560
+ return analyzeValueUse(memberAccess.expression, [], valuePath.environment, state, remainingDepth);
32561
+ }
32562
+ return analyzeValueUse(memberAccess.expression, valuePath.propertyPath.slice(memberAccess.propertyPath.length), valuePath.environment, state, remainingDepth);
32563
+ });
32564
+ state.activePaths.delete(activePathKey);
32565
+ return result;
32566
+ };
32567
+ const isCreateRefResultWriteOnly = (createRefCall, filename, scopes) => {
32568
+ if (!filename) return false;
32569
+ const program = findProgramRoot(createRefCall);
32570
+ if (!program) return false;
32571
+ const environment = {
32572
+ filename,
32573
+ program,
32574
+ scopes
32575
+ };
32576
+ const state = {
32577
+ activePaths: /* @__PURE__ */ new Set(),
32578
+ activeRenderedChildrenComponents: /* @__PURE__ */ new WeakSet(),
32579
+ environmentsByProgram: new WeakMap([[program, environment]])
32580
+ };
32581
+ const ownedValue = findOwnedSymbolValue(createRefCall, [], environment);
32582
+ return ownedValue ? analyzeSymbolValuePath(ownedValue, state, 12) : analyzeValueUse(createRefCall, [], environment, state, 12);
32583
+ };
32584
+ //#endregion
31991
32585
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
31992
- const MESSAGE$31 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
31993
- const isUseMemoCallbackArgument = (functionNode) => {
32586
+ const MESSAGE$31 = "`createRef()` may escape or be observed beyond the render that created it, so a later render can replace the ref object and detach the observed one. Hoist a `useRef()` call to the component's unconditional top level instead.";
32587
+ const isUseMemoCallbackArgument = (functionNode, scopes) => {
31994
32588
  const parent = functionNode.parent;
31995
32589
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
31996
32590
  if (parent.arguments?.[0] !== functionNode) return false;
31997
- return isReactFunctionCall(parent, "useMemo");
32591
+ return isReactApiCall(parent, "useMemo", scopes, { resolveNamedAliases: true });
31998
32592
  };
31999
- const findEnclosingRenderFunction = (node) => {
32593
+ const findEnclosingRenderFunction = (node, scopes) => {
32000
32594
  let enclosingFunction = findEnclosingFunction$1(node);
32001
- while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
32595
+ while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction, scopes)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
32002
32596
  return enclosingFunction;
32003
32597
  };
32004
32598
  const noCreateRefInFunctionComponent = defineRule({
@@ -32007,16 +32601,17 @@ const noCreateRefInFunctionComponent = defineRule({
32007
32601
  severity: "warn",
32008
32602
  recommendation: "Replace `createRef()` with the `useRef()` hook inside function components and hooks. `createRef` is only for class components.",
32009
32603
  create: (context) => ({ CallExpression(node) {
32010
- if (!isReactFunctionCall(node, "createRef")) return;
32011
- if (isNodeOfType(node.callee, "Identifier")) {
32012
- const symbol = context.scopes.symbolFor(node.callee);
32013
- if (symbol && symbol.kind !== "import") return;
32014
- }
32015
- const enclosingFunction = findEnclosingRenderFunction(node);
32604
+ if (!isReactApiCall(node, "createRef", context.scopes, {
32605
+ allowGlobalReactNamespace: true,
32606
+ allowUnboundBareCalls: true,
32607
+ resolveNamedAliases: true
32608
+ })) return;
32609
+ const enclosingFunction = findEnclosingRenderFunction(node, context.scopes);
32016
32610
  if (!enclosingFunction) return;
32017
32611
  const displayName = componentOrHookDisplayNameForFunction(enclosingFunction);
32018
32612
  if (!displayName) return;
32019
32613
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes, context.cfg))) return;
32614
+ if (isCreateRefResultWriteOnly(node, context.filename, context.scopes)) return;
32020
32615
  context.report({
32021
32616
  node,
32022
32617
  message: MESSAGE$31
@@ -38195,33 +38790,6 @@ const noLegacyClassLifecycles = defineRule({
38195
38790
  }
38196
38791
  });
38197
38792
  //#endregion
38198
- //#region src/plugin/utils/is-proven-react-class-component.ts
38199
- const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
38200
- const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
38201
- const expression = stripParenExpression(node);
38202
- if (isNodeOfType(expression, "MemberExpression")) {
38203
- const propertyName = getStaticPropertyName(expression);
38204
- const receiver = stripParenExpression(expression.object);
38205
- return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
38206
- }
38207
- if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
38208
- if (!isNodeOfType(expression, "Identifier")) return false;
38209
- const symbol = scopes.symbolFor(expression);
38210
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
38211
- visitedSymbolIds.add(symbol.id);
38212
- if (isImportedFromReact(symbol)) {
38213
- const importedName = getImportedName(symbol.declarationNode);
38214
- return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
38215
- }
38216
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
38217
- return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
38218
- };
38219
- const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
38220
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
38221
- visitedClassNodes.add(classNode);
38222
- return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
38223
- };
38224
- //#endregion
38225
38793
  //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
38226
38794
  const functionContainsProvenReactHookCall = (functionNode, scopes) => {
38227
38795
  if (!isFunctionLike$1(functionNode)) return false;
@@ -38685,7 +39253,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
38685
39253
  }
38686
39254
  if (isFunctionInvokedBeforeUsage(methodCallFunction, usageNode, usageBoundary, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes))) return true;
38687
39255
  }
38688
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
39256
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
38689
39257
  if (!bindingIdentifier) return false;
38690
39258
  const symbol = scopes.symbolFor(bindingIdentifier);
38691
39259
  if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
@@ -42992,6 +43560,31 @@ const noRenderReturnValue = defineRule({
42992
43560
  });
42993
43561
  //#endregion
42994
43562
  //#region src/plugin/rules/state-and-effects/no-reset-all-state-on-prop-change.ts
43563
+ const SYNCHRONOUS_ARRAY_CALLBACK_METHOD_NAMES = new Set([
43564
+ "every",
43565
+ "filter",
43566
+ "find",
43567
+ "findIndex",
43568
+ "flatMap",
43569
+ "forEach",
43570
+ "map",
43571
+ "reduce",
43572
+ "reduceRight",
43573
+ "some"
43574
+ ]);
43575
+ const createConstantFormula = (constantValue) => ({
43576
+ kind: "constant",
43577
+ constantValue
43578
+ });
43579
+ const createNotFormula = (formula) => ({
43580
+ kind: "not",
43581
+ left: formula
43582
+ });
43583
+ const createBinaryFormula = (kind, left, right) => ({
43584
+ kind,
43585
+ left,
43586
+ right
43587
+ });
42995
43588
  const isUndefinedNode = (node) => {
42996
43589
  if (node === null || node === void 0) return true;
42997
43590
  return isNodeOfType(node, "Identifier") && node.name === "undefined";
@@ -43053,6 +43646,241 @@ const getLivePropExpressionIdentity = (analysis, context, node, visitedSymbolIds
43053
43646
  }
43054
43647
  return null;
43055
43648
  };
43649
+ const getLivePropAtomKey = (identity) => `prop:${identity.propSymbolId}:${JSON.stringify(identity.memberPath)}:${identity.booleanNormalization}`;
43650
+ const getBooleanFormula$1 = (analysis, context, node, protectedSymbolIds, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
43651
+ const expression = stripParenExpression(node);
43652
+ if (isNodeOfType(expression, "Literal")) return createConstantFormula(Boolean(expression.value));
43653
+ if (isNodeOfType(expression, "Identifier")) {
43654
+ if (expression.name === "undefined" && context.scopes.isGlobalReference(expression)) return createConstantFormula(false);
43655
+ const symbol = context.scopes.symbolFor(expression);
43656
+ if (!symbol) return null;
43657
+ if (!protectedSymbolIds.has(symbol.id) && symbol.kind === "const" && !visitedSymbolIds.has(symbol.id) && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && symbol.declarationNode.id === symbol.bindingIdentifier) {
43658
+ const initializer = getDirectConstInitializer(symbol);
43659
+ if (initializer) {
43660
+ visitedSymbolIds.add(symbol.id);
43661
+ const initializerFormula = getBooleanFormula$1(analysis, context, initializer, protectedSymbolIds, visitedSymbolIds);
43662
+ if (initializerFormula) return initializerFormula;
43663
+ }
43664
+ }
43665
+ return {
43666
+ kind: "atom",
43667
+ atomKey: `symbol:${symbol.id}`
43668
+ };
43669
+ }
43670
+ if (isNodeOfType(expression, "MemberExpression")) {
43671
+ const identity = getLivePropExpressionIdentity(analysis, context, expression);
43672
+ return identity ? {
43673
+ kind: "atom",
43674
+ atomKey: getLivePropAtomKey(identity)
43675
+ } : null;
43676
+ }
43677
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
43678
+ const argumentFormula = getBooleanFormula$1(analysis, context, expression.argument, protectedSymbolIds, visitedSymbolIds);
43679
+ return argumentFormula ? createNotFormula(argumentFormula) : null;
43680
+ }
43681
+ if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee) && expression.arguments.length === 1 && !isNodeOfType(expression.arguments[0], "SpreadElement")) return getBooleanFormula$1(analysis, context, expression.arguments[0], protectedSymbolIds, visitedSymbolIds);
43682
+ if (isNodeOfType(expression, "LogicalExpression") && (expression.operator === "&&" || expression.operator === "||")) {
43683
+ const leftFormula = getBooleanFormula$1(analysis, context, expression.left, protectedSymbolIds, new Set(visitedSymbolIds));
43684
+ const rightFormula = getBooleanFormula$1(analysis, context, expression.right, protectedSymbolIds, new Set(visitedSymbolIds));
43685
+ if (!leftFormula || !rightFormula) return null;
43686
+ return createBinaryFormula(expression.operator === "&&" ? "and" : "or", leftFormula, rightFormula);
43687
+ }
43688
+ if (isNodeOfType(expression, "ConditionalExpression")) {
43689
+ const testFormula = getBooleanFormula$1(analysis, context, expression.test, protectedSymbolIds, new Set(visitedSymbolIds));
43690
+ const consequentFormula = getBooleanFormula$1(analysis, context, expression.consequent, protectedSymbolIds, new Set(visitedSymbolIds));
43691
+ const alternateFormula = getBooleanFormula$1(analysis, context, expression.alternate, protectedSymbolIds, new Set(visitedSymbolIds));
43692
+ if (!testFormula || !consequentFormula || !alternateFormula) return null;
43693
+ return createBinaryFormula("or", createBinaryFormula("and", testFormula, consequentFormula), createBinaryFormula("and", createNotFormula(testFormula), alternateFormula));
43694
+ }
43695
+ if (isNodeOfType(expression, "BinaryExpression") && (expression.operator === "===" || expression.operator === "==" || expression.operator === "!==" || expression.operator === "!=")) {
43696
+ const leftIsBoolean = isNodeOfType(expression.left, "Literal") && typeof expression.left.value === "boolean";
43697
+ if (leftIsBoolean === (isNodeOfType(expression.right, "Literal") && typeof expression.right.value === "boolean")) return null;
43698
+ const booleanLiteral = leftIsBoolean ? expression.left : expression.right;
43699
+ const comparedFormula = getBooleanFormula$1(analysis, context, leftIsBoolean ? expression.right : expression.left, protectedSymbolIds, visitedSymbolIds);
43700
+ if (!comparedFormula || !isNodeOfType(booleanLiteral, "Literal")) return null;
43701
+ return (expression.operator === "===" || expression.operator === "==" ? booleanLiteral.value : !booleanLiteral.value) ? comparedFormula : createNotFormula(comparedFormula);
43702
+ }
43703
+ return null;
43704
+ };
43705
+ const getRequiredTruthyConditions = (analysis, context, node, protectedSymbolIds) => {
43706
+ const formula = getBooleanFormula$1(analysis, context, node, protectedSymbolIds);
43707
+ if (formula) return [formula];
43708
+ const expression = stripParenExpression(node);
43709
+ if (!isNodeOfType(expression, "LogicalExpression") || expression.operator !== "&&") return [];
43710
+ return [...getRequiredTruthyConditions(analysis, context, expression.left, protectedSymbolIds), ...getRequiredTruthyConditions(analysis, context, expression.right, protectedSymbolIds)];
43711
+ };
43712
+ const evaluateBooleanFormula$1 = (formula, assignments) => {
43713
+ if (formula.kind === "constant") return formula.constantValue ?? null;
43714
+ if (formula.kind === "atom") return formula.atomKey === void 0 ? null : assignments.get(formula.atomKey) ?? null;
43715
+ if (formula.kind === "not") {
43716
+ if (!formula.left) return null;
43717
+ const value = evaluateBooleanFormula$1(formula.left, assignments);
43718
+ return value === null ? null : !value;
43719
+ }
43720
+ if (!formula.left || !formula.right) return null;
43721
+ const leftValue = evaluateBooleanFormula$1(formula.left, assignments);
43722
+ const rightValue = evaluateBooleanFormula$1(formula.right, assignments);
43723
+ if (formula.kind === "and") {
43724
+ if (leftValue === false || rightValue === false) return false;
43725
+ return leftValue === true && rightValue === true ? true : null;
43726
+ }
43727
+ if (leftValue === true || rightValue === true) return true;
43728
+ return leftValue === false && rightValue === false ? false : null;
43729
+ };
43730
+ const assignBooleanFact = (facts, atomKey, value) => {
43731
+ const existingValue = facts.assignments.get(atomKey);
43732
+ if (existingValue === void 0) {
43733
+ facts.assignments.set(atomKey, value);
43734
+ facts.didChange = true;
43735
+ } else if (existingValue !== value) facts.didConflict = true;
43736
+ };
43737
+ const addRequiredBooleanFacts = (formula, expectedValue, facts) => {
43738
+ const existingValue = evaluateBooleanFormula$1(formula, facts.assignments);
43739
+ if (existingValue !== null) {
43740
+ if (existingValue !== expectedValue) facts.didConflict = true;
43741
+ return;
43742
+ }
43743
+ if (formula.kind === "atom" && formula.atomKey !== void 0) {
43744
+ assignBooleanFact(facts, formula.atomKey, expectedValue);
43745
+ return;
43746
+ }
43747
+ if (formula.kind === "not" && formula.left) {
43748
+ addRequiredBooleanFacts(formula.left, !expectedValue, facts);
43749
+ return;
43750
+ }
43751
+ if (!formula.left || !formula.right) return;
43752
+ if (formula.kind === "and") {
43753
+ if (expectedValue) {
43754
+ addRequiredBooleanFacts(formula.left, true, facts);
43755
+ addRequiredBooleanFacts(formula.right, true, facts);
43756
+ return;
43757
+ }
43758
+ const leftValue = evaluateBooleanFormula$1(formula.left, facts.assignments);
43759
+ const rightValue = evaluateBooleanFormula$1(formula.right, facts.assignments);
43760
+ if (leftValue === true) addRequiredBooleanFacts(formula.right, false, facts);
43761
+ if (rightValue === true) addRequiredBooleanFacts(formula.left, false, facts);
43762
+ return;
43763
+ }
43764
+ if (!expectedValue) {
43765
+ addRequiredBooleanFacts(formula.left, false, facts);
43766
+ addRequiredBooleanFacts(formula.right, false, facts);
43767
+ return;
43768
+ }
43769
+ const leftValue = evaluateBooleanFormula$1(formula.left, facts.assignments);
43770
+ const rightValue = evaluateBooleanFormula$1(formula.right, facts.assignments);
43771
+ if (leftValue === false) addRequiredBooleanFacts(formula.right, true, facts);
43772
+ if (rightValue === false) addRequiredBooleanFacts(formula.left, true, facts);
43773
+ };
43774
+ const doConditionsImplyFormula = (conditions, target) => {
43775
+ const facts = {
43776
+ assignments: /* @__PURE__ */ new Map(),
43777
+ didConflict: false,
43778
+ didChange: true
43779
+ };
43780
+ while (facts.didChange && !facts.didConflict) {
43781
+ facts.didChange = false;
43782
+ for (const condition of conditions) addRequiredBooleanFacts(condition, true, facts);
43783
+ }
43784
+ return facts.didConflict || evaluateBooleanFormula$1(target, facts.assignments) === true;
43785
+ };
43786
+ const getFunctionBindingSymbol = (functionNode, scopes) => {
43787
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) return scopes.symbolFor(functionNode.id);
43788
+ const parent = functionNode.parent;
43789
+ if ((isNodeOfType(functionNode, "ArrowFunctionExpression") || isNodeOfType(functionNode, "FunctionExpression")) && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return scopes.symbolFor(parent.id);
43790
+ return null;
43791
+ };
43792
+ const getComponentFunctionNode = (containingNode) => {
43793
+ if (isFunctionLike$1(containingNode)) return containingNode;
43794
+ if (!isNodeOfType(containingNode, "VariableDeclarator") || !containingNode.init) return null;
43795
+ const initializer = stripParenExpression(containingNode.init);
43796
+ if (isFunctionLike$1(initializer)) return initializer;
43797
+ if (!isNodeOfType(initializer, "CallExpression")) return null;
43798
+ const firstArgument = initializer.arguments[0];
43799
+ return firstArgument && !isNodeOfType(firstArgument, "SpreadElement") && isFunctionLike$1(firstArgument) ? firstArgument : null;
43800
+ };
43801
+ const isReferenceDirectlyCalled = (identifier) => {
43802
+ const unwrappedIdentifier = stripParenExpression(identifier);
43803
+ const parent = unwrappedIdentifier.parent;
43804
+ return isNodeOfType(parent, "CallExpression") && parent.callee === unwrappedIdentifier ? parent : null;
43805
+ };
43806
+ const getSynchronousCallbackCall = (functionNode) => {
43807
+ const callExpression = functionNode.parent;
43808
+ if (!isNodeOfType(callExpression, "CallExpression") || !callExpression.arguments.some((argument) => argument === functionNode) || !isNodeOfType(callExpression.callee, "MemberExpression")) return null;
43809
+ const methodName = getStaticMemberPropertyName(callExpression.callee);
43810
+ return methodName && SYNCHRONOUS_ARRAY_CALLBACK_METHOD_NAMES.has(methodName) ? callExpression : null;
43811
+ };
43812
+ const isNodeEvaluatedDuringRender = (node, componentNode, scopes, visitedFunctionSymbolIds = /* @__PURE__ */ new Set()) => {
43813
+ const functionNode = findEnclosingFunction$1(node);
43814
+ if (!functionNode) return false;
43815
+ if (functionNode === componentNode) return true;
43816
+ const synchronousCallbackCall = getSynchronousCallbackCall(functionNode);
43817
+ if (synchronousCallbackCall) return isNodeEvaluatedDuringRender(synchronousCallbackCall, componentNode, scopes, visitedFunctionSymbolIds);
43818
+ if (executesDuringRender(functionNode, scopes)) return isNodeEvaluatedDuringRender(functionNode.parent ?? functionNode, componentNode, scopes, visitedFunctionSymbolIds);
43819
+ const functionSymbol = getFunctionBindingSymbol(functionNode, scopes);
43820
+ if (!functionSymbol || visitedFunctionSymbolIds.has(functionSymbol.id)) return false;
43821
+ visitedFunctionSymbolIds.add(functionSymbol.id);
43822
+ let callCount = 0;
43823
+ for (const reference of functionSymbol.references) {
43824
+ const callExpression = isReferenceDirectlyCalled(reference.identifier);
43825
+ if (!callExpression) return false;
43826
+ if (!isNodeEvaluatedDuringRender(callExpression, componentNode, scopes, new Set(visitedFunctionSymbolIds))) return false;
43827
+ callCount += 1;
43828
+ }
43829
+ return callCount > 0;
43830
+ };
43831
+ const isInlineJsxCallback = (functionNode) => {
43832
+ let ancestor = functionNode.parent;
43833
+ while (ancestor) {
43834
+ if (isNodeOfType(ancestor, "JSXAttribute")) return true;
43835
+ if (isFunctionLike$1(ancestor) || isNodeOfType(ancestor, "Program")) return false;
43836
+ ancestor = ancestor.parent;
43837
+ }
43838
+ return false;
43839
+ };
43840
+ const collectExposureConditions = (analysis, context, node, componentNode, protectedSymbolIds) => {
43841
+ const conditions = [];
43842
+ let child = node;
43843
+ let parent = node.parent;
43844
+ while (parent) {
43845
+ if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) {
43846
+ if (parent.operator === "&&") conditions.push(...getRequiredTruthyConditions(analysis, context, parent.left, protectedSymbolIds));
43847
+ if (parent.operator === "||") {
43848
+ const leftFormula = getBooleanFormula$1(analysis, context, parent.left, protectedSymbolIds);
43849
+ if (leftFormula) conditions.push(createNotFormula(leftFormula));
43850
+ }
43851
+ } else if (isNodeOfType(parent, "ConditionalExpression")) {
43852
+ const testFormula = getBooleanFormula$1(analysis, context, parent.test, protectedSymbolIds);
43853
+ if (testFormula && parent.consequent === child) conditions.push(testFormula);
43854
+ if (testFormula && parent.alternate === child) conditions.push(createNotFormula(testFormula));
43855
+ } else if (isNodeOfType(parent, "IfStatement")) {
43856
+ const testFormula = getBooleanFormula$1(analysis, context, parent.test, protectedSymbolIds);
43857
+ if (testFormula && parent.consequent === child) conditions.push(testFormula);
43858
+ if (testFormula && parent.alternate === child) conditions.push(createNotFormula(testFormula));
43859
+ }
43860
+ if (parent === componentNode) break;
43861
+ if (isFunctionLike$1(parent) && parent !== componentNode && !isInlineJsxCallback(parent)) {
43862
+ const synchronousCallbackCall = getSynchronousCallbackCall(parent);
43863
+ if (synchronousCallbackCall) {
43864
+ child = synchronousCallbackCall;
43865
+ parent = synchronousCallbackCall.parent;
43866
+ continue;
43867
+ }
43868
+ const functionSymbol = getFunctionBindingSymbol(parent, context.scopes);
43869
+ if (functionSymbol?.references.length === 1) {
43870
+ const callExpression = isReferenceDirectlyCalled(functionSymbol.references[0].identifier);
43871
+ if (callExpression) {
43872
+ child = callExpression;
43873
+ parent = callExpression.parent;
43874
+ continue;
43875
+ }
43876
+ }
43877
+ break;
43878
+ }
43879
+ child = parent;
43880
+ parent = parent.parent;
43881
+ }
43882
+ return conditions;
43883
+ };
43056
43884
  const isMountSnapshotInitializer = (context, node) => {
43057
43885
  if (!isReactApiCall(node, "useMemo", context.scopes, { resolveNamedAliases: true }) || !isNodeOfType(node, "CallExpression")) return false;
43058
43886
  const dependencies = node.arguments?.[1];
@@ -43128,6 +43956,158 @@ const countUseStates = (analysis, componentNode) => {
43128
43956
  for (const ref of getDownstreamRefs(analysis, componentNode)) if (isState(analysis, ref)) stateVariables.add(ref.resolved);
43129
43957
  return stateVariables.size;
43130
43958
  };
43959
+ const getStateSymbolForSetter = (analysis, context, setterReference) => {
43960
+ const useStateDeclaration = getUseStateDecl(analysis, setterReference);
43961
+ if (!useStateDeclaration || !isNodeOfType(useStateDeclaration, "VariableDeclarator") || !isNodeOfType(useStateDeclaration.id, "ArrayPattern")) return null;
43962
+ const stateBinding = useStateDeclaration.id.elements[0];
43963
+ return stateBinding && isNodeOfType(stateBinding, "Identifier") ? context.scopes.symbolFor(stateBinding) : null;
43964
+ };
43965
+ const getPropertyName = (node) => {
43966
+ if (isNodeOfType(node, "Identifier")) return node.name;
43967
+ return isNodeOfType(node, "Literal") && typeof node.value === "string" ? node.value : null;
43968
+ };
43969
+ const isBooleanTypeNode = (node) => {
43970
+ if (!node) return false;
43971
+ if (isNodeOfType(node, "TSBooleanKeyword")) return true;
43972
+ if (!isNodeOfType(node, "TSUnionType")) return false;
43973
+ return node.types.every((typeNode) => isNodeOfType(typeNode, "TSBooleanKeyword") || isNodeOfType(typeNode, "TSUndefinedKeyword") || isNodeOfType(typeNode, "TSNullKeyword"));
43974
+ };
43975
+ const getBooleanPropertyType = (typeNode, propertyName, referenceNode) => {
43976
+ const unwrappedType = isNodeOfType(typeNode, "TSTypeAnnotation") ? typeNode.typeAnnotation : typeNode;
43977
+ if (isNodeOfType(unwrappedType, "TSTypeLiteral")) return unwrappedType.members.some((member) => isNodeOfType(member, "TSPropertySignature") && getPropertyName(member.key) === propertyName && isBooleanTypeNode(member.typeAnnotation?.typeAnnotation));
43978
+ if (!isNodeOfType(unwrappedType, "TSTypeReference") || !isNodeOfType(unwrappedType.typeName, "Identifier")) return false;
43979
+ const typeName = unwrappedType.typeName.name;
43980
+ if (hasEnclosingTypeParameterNamed(referenceNode, typeName)) return false;
43981
+ const programNode = findProgramNode(referenceNode);
43982
+ if (!isNodeOfType(programNode, "Program")) return false;
43983
+ const matchingInterfaces = programNode.body.flatMap((statement) => {
43984
+ const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
43985
+ return isNodeOfType(declaration, "TSInterfaceDeclaration") && declaration.id.name === typeName ? [declaration] : [];
43986
+ });
43987
+ if (matchingInterfaces.length !== 1) return false;
43988
+ let sameNameTypeBindingCount = 0;
43989
+ walkAst(programNode, (candidate) => {
43990
+ const identifier = isNodeOfType(candidate, "TSInterfaceDeclaration") || isNodeOfType(candidate, "TSTypeAliasDeclaration") || isNodeOfType(candidate, "ClassDeclaration") || isNodeOfType(candidate, "ClassExpression") || isNodeOfType(candidate, "TSEnumDeclaration") ? candidate.id : isNodeOfType(candidate, "TSTypeParameter") ? candidate.name : null;
43991
+ if (isNodeOfType(identifier, "Identifier") && identifier.name === typeName) sameNameTypeBindingCount += 1;
43992
+ });
43993
+ if (sameNameTypeBindingCount !== 1) return false;
43994
+ return matchingInterfaces[0].body.body.some((member) => isNodeOfType(member, "TSPropertySignature") && getPropertyName(member.key) === propertyName && isBooleanTypeNode(member.typeAnnotation?.typeAnnotation));
43995
+ };
43996
+ const findProgramNode = (node) => {
43997
+ let currentNode = node;
43998
+ while (currentNode.parent) currentNode = currentNode.parent;
43999
+ return currentNode;
44000
+ };
44001
+ const hasBooleanBindingAnnotation = (symbol, identifier) => {
44002
+ const property = symbol.bindingIdentifier.parent;
44003
+ const objectPattern = property?.parent;
44004
+ if (!isNodeOfType(property, "Property") || !isNodeOfType(objectPattern, "ObjectPattern") || !objectPattern.typeAnnotation) return false;
44005
+ const propertyName = getPropertyName(property.key);
44006
+ return Boolean(propertyName && getBooleanPropertyType(objectPattern.typeAnnotation, propertyName, identifier));
44007
+ };
44008
+ const isBooleanExpression$1 = (context, node, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
44009
+ const expression = stripParenExpression(node);
44010
+ if (isNodeOfType(expression, "Literal")) return typeof expression.value === "boolean";
44011
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return true;
44012
+ if (isNodeOfType(expression, "BinaryExpression")) return [
44013
+ "==",
44014
+ "===",
44015
+ "!=",
44016
+ "!==",
44017
+ "<",
44018
+ "<=",
44019
+ ">",
44020
+ ">="
44021
+ ].includes(expression.operator);
44022
+ if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee)) return true;
44023
+ if (isNodeOfType(expression, "LogicalExpression")) return isBooleanExpression$1(context, expression.left, new Set(visitedSymbolIds)) && isBooleanExpression$1(context, expression.right, new Set(visitedSymbolIds));
44024
+ if (isNodeOfType(expression, "ConditionalExpression")) return isBooleanExpression$1(context, expression.consequent, new Set(visitedSymbolIds)) && isBooleanExpression$1(context, expression.alternate, new Set(visitedSymbolIds));
44025
+ if (!isNodeOfType(expression, "Identifier")) return false;
44026
+ const symbol = context.scopes.symbolFor(expression);
44027
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
44028
+ if (hasBooleanBindingAnnotation(symbol, expression)) return true;
44029
+ visitedSymbolIds.add(symbol.id);
44030
+ const initializer = getDirectConstInitializer(symbol);
44031
+ return Boolean(initializer && isBooleanExpression$1(context, initializer, visitedSymbolIds));
44032
+ };
44033
+ const getPropDerivedDependencySymbols = (analysis, context, dependencyReferences) => {
44034
+ const symbolsById = /* @__PURE__ */ new Map();
44035
+ for (const dependencyReference of dependencyReferences) {
44036
+ if (!getUpstreamRefs(analysis, dependencyReference).some((upstreamReference) => isProp(analysis, upstreamReference))) continue;
44037
+ const symbol = context.scopes.symbolFor(dependencyReference.identifier);
44038
+ if (symbol) symbolsById.set(symbol.id, symbol);
44039
+ }
44040
+ return [...symbolsById.values()];
44041
+ };
44042
+ const getImpliedDependencyValue = (conditions, dependencyFormulas) => {
44043
+ const impliesTrue = dependencyFormulas.some((dependencyFormula) => doConditionsImplyFormula(conditions, dependencyFormula));
44044
+ if (impliesTrue === dependencyFormulas.some((dependencyFormula) => doConditionsImplyFormula(conditions, createNotFormula(dependencyFormula)))) return null;
44045
+ return impliesTrue;
44046
+ };
44047
+ const getSetterExposureConditions = (analysis, context, setterReference, componentNode, protectedSymbolIds) => {
44048
+ const functionNode = findEnclosingFunction$1(setterReference.identifier);
44049
+ if (!functionNode) return null;
44050
+ if (isInlineJsxCallback(functionNode)) return [collectExposureConditions(analysis, context, functionNode, componentNode, protectedSymbolIds)];
44051
+ const functionSymbol = getFunctionBindingSymbol(functionNode, context.scopes);
44052
+ if (!functionSymbol || functionSymbol.references.length === 0) return null;
44053
+ const conditionsByReference = [];
44054
+ for (const reference of functionSymbol.references) {
44055
+ if (isReferenceDirectlyCalled(reference.identifier)) return null;
44056
+ let ancestor = reference.identifier.parent;
44057
+ while (ancestor && !isNodeOfType(ancestor, "JSXAttribute") && !isFunctionLike$1(ancestor)) ancestor = ancestor.parent;
44058
+ if (!isNodeOfType(ancestor, "JSXAttribute")) return null;
44059
+ conditionsByReference.push(collectExposureConditions(analysis, context, reference.identifier, componentNode, protectedSymbolIds));
44060
+ }
44061
+ return conditionsByReference;
44062
+ };
44063
+ const areAllSetterWritesVisibilityGuarded = (analysis, context, componentNode, resetSetterReferences, dependencySymbolIds, dependencyFormulas, visibleDependencyValue) => {
44064
+ const resetIdentifiers = new Set(resetSetterReferences.map((setterReference) => setterReference.identifier));
44065
+ const setterVariables = new Set(resetSetterReferences.map((reference) => reference.resolved));
44066
+ for (const setterVariable of setterVariables) {
44067
+ if (!setterVariable) return false;
44068
+ for (const setterReference of setterVariable.references) {
44069
+ if (setterVariable.identifiers.some((identifier) => identifier === setterReference.identifier)) continue;
44070
+ if (resetIdentifiers.has(setterReference.identifier)) continue;
44071
+ if (!getCallExpr(setterReference)) return false;
44072
+ const conditionsByExposure = getSetterExposureConditions(analysis, context, setterReference, componentNode, dependencySymbolIds);
44073
+ if (!conditionsByExposure || conditionsByExposure.some((conditions) => getImpliedDependencyValue(conditions, dependencyFormulas) !== visibleDependencyValue)) return false;
44074
+ }
44075
+ }
44076
+ return true;
44077
+ };
44078
+ const areAllResetStateReadsHiddenUntilReset = (analysis, context, componentNode, setterReferences, dependencyReferences) => {
44079
+ if (dependencyReferences.length !== 1) return false;
44080
+ const dependencySymbols = getPropDerivedDependencySymbols(analysis, context, dependencyReferences);
44081
+ if (dependencySymbols.length === 0 || dependencySymbols.some((symbol) => !isBooleanExpression$1(context, symbol.bindingIdentifier) || symbol.references.some((reference) => reference.flag !== "read"))) return false;
44082
+ const dependencySymbolIds = new Set(dependencySymbols.map((symbol) => symbol.id));
44083
+ const dependencyFormulas = dependencySymbols.map((symbol) => ({
44084
+ kind: "atom",
44085
+ atomKey: `symbol:${symbol.id}`
44086
+ }));
44087
+ const resetStateSymbolsById = /* @__PURE__ */ new Map();
44088
+ for (const setterReference of setterReferences) {
44089
+ const stateSymbol = getStateSymbolForSetter(analysis, context, setterReference);
44090
+ if (!stateSymbol) return false;
44091
+ resetStateSymbolsById.set(stateSymbol.id, stateSymbol);
44092
+ }
44093
+ let visibleDependencyValue = null;
44094
+ for (const stateSymbol of resetStateSymbolsById.values()) {
44095
+ let exposedReadCount = 0;
44096
+ for (const reference of stateSymbol.references) {
44097
+ if (reference.flag === "write") continue;
44098
+ const isRenderRead = isNodeEvaluatedDuringRender(reference.identifier, componentNode, context.scopes);
44099
+ const functionNode = findEnclosingFunction$1(reference.identifier);
44100
+ if (!isRenderRead && functionNode && !isInlineJsxCallback(functionNode)) return false;
44101
+ const referenceDependencyValue = getImpliedDependencyValue(collectExposureConditions(analysis, context, reference.identifier, componentNode, dependencySymbolIds), dependencyFormulas);
44102
+ if (referenceDependencyValue === null) return false;
44103
+ if (visibleDependencyValue !== null && visibleDependencyValue !== referenceDependencyValue) return false;
44104
+ visibleDependencyValue = referenceDependencyValue;
44105
+ exposedReadCount += 1;
44106
+ }
44107
+ if (exposedReadCount === 0) return false;
44108
+ }
44109
+ return Boolean(resetStateSymbolsById.size > 0 && visibleDependencyValue !== null && areAllSetterWritesVisibilityGuarded(analysis, context, componentNode, setterReferences, dependencySymbolIds, dependencyFormulas, visibleDependencyValue));
44110
+ };
43131
44111
  const findPropUsedToResetAllState = (analysis, context, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
43132
44112
  const stateSetterRefs = effectFnRefs.filter((ref) => isSyncStateSetterCall(analysis, ref, effectFn));
43133
44113
  if (stateSetterRefs.length === 0) return null;
@@ -43135,6 +44115,8 @@ const findPropUsedToResetAllState = (analysis, context, effectFnRefs, depsRefs,
43135
44115
  if (stateSetterRefs.every((setterRef) => effectFnRefs.some((otherRef) => otherRef !== setterRef && otherRef.resolved === setterRef.resolved && Boolean(getCallExpr(otherRef)) && !isSyncStateSetterCall(analysis, otherRef, effectFn)))) return null;
43136
44116
  const containing = findContainingNode(analysis, useEffectNode);
43137
44117
  if (new Set(stateSetterRefs.map((setterRef) => setterRef.resolved)).size !== countUseStates(analysis, containing)) return null;
44118
+ const componentFunctionNode = containing ? getComponentFunctionNode(containing) : null;
44119
+ if (componentFunctionNode && areAllResetStateReadsHiddenUntilReset(analysis, context, componentFunctionNode, stateSetterRefs, depsRefs)) return null;
43138
44120
  for (const depRef of depsRefs) for (const upRef of getUpstreamRefs(analysis, depRef)) if (isProp(analysis, upRef)) return upRef;
43139
44121
  return null;
43140
44122
  };
@@ -62551,7 +63533,7 @@ const tanstackStartNoNavigateInRender = defineRule({
62551
63533
  return false;
62552
63534
  };
62553
63535
  const isWiredAsEventHandler = (functionNode) => {
62554
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
63536
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
62555
63537
  if (!bindingIdentifier) return false;
62556
63538
  const bindingSymbol = context.scopes.symbolFor(bindingIdentifier);
62557
63539
  if (!bindingSymbol) return false;
@@ -62571,7 +63553,7 @@ const tanstackStartNoNavigateInRender = defineRule({
62571
63553
  return false;
62572
63554
  };
62573
63555
  const isSynchronouslyInvokedAnonymousWrapper = (functionNode) => {
62574
- if (getFunctionBindingIdentifier(functionNode)) return false;
63556
+ if (getFunctionBindingIdentifier$1(functionNode)) return false;
62575
63557
  const parent = functionNode.parent;
62576
63558
  if (!isNodeOfType(parent, "CallExpression")) return false;
62577
63559
  return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
@@ -68503,6 +69485,7 @@ const CROSS_FILE_RULE_IDS = new Set([
68503
69485
  "no-indeterminate-attribute",
68504
69486
  "no-locale-format-in-render",
68505
69487
  "no-match-media-in-state-initializer",
69488
+ "no-create-ref-in-function-component",
68506
69489
  "no-adjust-state-on-prop-change",
68507
69490
  "no-derived-state",
68508
69491
  "no-derived-state-effect",
@@ -68607,6 +69590,18 @@ const collectForwardedHookDependencies = ({ absoluteFilePath, staticImports }) =
68607
69590
  collectProgramDependencies(resolved.filePath, resolved.programNode, 4);
68608
69591
  }
68609
69592
  };
69593
+ const collectCreateRefDependencies = ({ absoluteFilePath, program }) => {
69594
+ attachParentReferences(program);
69595
+ const scopes = analyzeScopes(program);
69596
+ walkAst(program, (node) => {
69597
+ if (!isNodeOfType(node, "CallExpression") || !isReactApiCall(node, "createRef", scopes, {
69598
+ allowGlobalReactNamespace: true,
69599
+ allowUnboundBareCalls: true,
69600
+ resolveNamedAliases: true
69601
+ })) return;
69602
+ isCreateRefResultWriteOnly(node, absoluteFilePath, scopes);
69603
+ });
69604
+ };
68610
69605
  const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
68611
69606
  const namedUseReducerLocals = /* @__PURE__ */ new Set();
68612
69607
  const reactObjectLocals = /* @__PURE__ */ new Set();
@@ -68695,6 +69690,7 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
68695
69690
  ["no-indeterminate-attribute", collectNearestManifestDependencies],
68696
69691
  ["no-locale-format-in-render", collectNearestManifestDependencies],
68697
69692
  ["no-match-media-in-state-initializer", collectNearestManifestDependencies],
69693
+ ["no-create-ref-in-function-component", collectCreateRefDependencies],
68698
69694
  ["no-adjust-state-on-prop-change", collectEffectValueHelperDependencies],
68699
69695
  ["no-derived-state", collectEffectValueHelperDependencies],
68700
69696
  ["no-derived-state-effect", collectEffectValueHelperDependencies],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.7.8-dev.ece35b3",
3
+ "version": "0.7.9-dev.194cc06",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",