oxlint-plugin-react-doctor 0.7.8 → 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.
- package/dist/index.js +1045 -49
- 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()`
|
|
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
|
|
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 (!
|
|
32011
|
-
|
|
32012
|
-
|
|
32013
|
-
|
|
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],
|