oxlint-plugin-react-doctor 0.7.7-dev.a468589 → 0.7.7-dev.af58965
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 +1707 -796
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1194,7 +1194,7 @@ const findVariableInitializer = (referenceNode, bindingName) => {
|
|
|
1194
1194
|
* (`FUNCTION_LIKE_TYPES.has(node.type)`) and as a type-guard. The
|
|
1195
1195
|
* type-guard form covers both shapes without callers paying a cast.
|
|
1196
1196
|
*/
|
|
1197
|
-
const isFunctionLike$
|
|
1197
|
+
const isFunctionLike$1 = (node) => Boolean(node && (isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "FunctionDeclaration")));
|
|
1198
1198
|
//#endregion
|
|
1199
1199
|
//#region src/plugin/utils/strip-paren-expression.ts
|
|
1200
1200
|
const TRANSPARENT_EXPRESSION_WRAPPER_TYPES = new Set([
|
|
@@ -1230,13 +1230,13 @@ const resolveConstIdentifierAlias = (identifier, scopes) => {
|
|
|
1230
1230
|
//#region src/plugin/utils/resolve-exact-local-function.ts
|
|
1231
1231
|
const resolveExactLocalFunction = (expression, scopes) => {
|
|
1232
1232
|
const unwrappedExpression = stripParenExpression(expression);
|
|
1233
|
-
if (isFunctionLike$
|
|
1233
|
+
if (isFunctionLike$1(unwrappedExpression)) return unwrappedExpression;
|
|
1234
1234
|
if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
|
|
1235
1235
|
const symbol = resolveConstIdentifierAlias(unwrappedExpression, scopes);
|
|
1236
|
-
if (symbol?.kind === "function") return !symbol.references.some((reference) => reference.flag !== "read") && isFunctionLike$
|
|
1236
|
+
if (symbol?.kind === "function") return !symbol.references.some((reference) => reference.flag !== "read") && isFunctionLike$1(symbol.declarationNode) ? symbol.declarationNode : null;
|
|
1237
1237
|
if (symbol?.kind !== "const" || !symbol.initializer) return null;
|
|
1238
1238
|
const unwrappedInitializer = stripParenExpression(symbol.initializer);
|
|
1239
|
-
return isFunctionLike$
|
|
1239
|
+
return isFunctionLike$1(unwrappedInitializer) ? unwrappedInitializer : null;
|
|
1240
1240
|
};
|
|
1241
1241
|
//#endregion
|
|
1242
1242
|
//#region src/plugin/utils/get-effect-callback.ts
|
|
@@ -1921,9 +1921,9 @@ const isComponentIdentifierName = (name) => {
|
|
|
1921
1921
|
const firstCharacter = name[0];
|
|
1922
1922
|
return Boolean(firstCharacter && firstCharacter === firstCharacter.toUpperCase());
|
|
1923
1923
|
};
|
|
1924
|
-
const isFunctionLike
|
|
1924
|
+
const isFunctionLike = (node) => Boolean(node && (isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ArrowFunctionExpression")));
|
|
1925
1925
|
const markFunctionReturnJsx = (functionNode, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
|
|
1926
|
-
if (!isFunctionLike
|
|
1926
|
+
if (!isFunctionLike(functionNode)) return;
|
|
1927
1927
|
if (isNodeOfType(functionNode, "ArrowFunctionExpression")) {
|
|
1928
1928
|
const body = stripParenExpression(functionNode.body);
|
|
1929
1929
|
if (!isNodeOfType(body, "BlockStatement")) {
|
|
@@ -1934,7 +1934,7 @@ const markFunctionReturnJsx = (functionNode, programRoot, generatedImageJsxNodes
|
|
|
1934
1934
|
const body = functionNode.body;
|
|
1935
1935
|
if (!isNodeOfType(body, "BlockStatement")) return;
|
|
1936
1936
|
walkAst(body, (descendantNode) => {
|
|
1937
|
-
if (descendantNode !== body && isFunctionLike
|
|
1937
|
+
if (descendantNode !== body && isFunctionLike(descendantNode)) return false;
|
|
1938
1938
|
if (!isNodeOfType(descendantNode, "ReturnStatement")) return;
|
|
1939
1939
|
if (!descendantNode.argument) return;
|
|
1940
1940
|
markGeneratedImageExpression(stripParenExpression(descendantNode.argument), programRoot, generatedImageJsxNodes, visitedComponentNames);
|
|
@@ -1996,7 +1996,7 @@ const markGeneratedImageExpression = (expression, programRoot, generatedImageJsx
|
|
|
1996
1996
|
markJsxSubtree(unwrappedExpression, programRoot, generatedImageJsxNodes, visitedComponentNames);
|
|
1997
1997
|
return;
|
|
1998
1998
|
}
|
|
1999
|
-
if (isFunctionLike
|
|
1999
|
+
if (isFunctionLike(unwrappedExpression)) {
|
|
2000
2000
|
markFunctionReturnJsx(unwrappedExpression, programRoot, generatedImageJsxNodes, visitedComponentNames);
|
|
2001
2001
|
return;
|
|
2002
2002
|
}
|
|
@@ -2012,7 +2012,7 @@ const markGeneratedImageExpression = (expression, programRoot, generatedImageJsx
|
|
|
2012
2012
|
}
|
|
2013
2013
|
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
2014
2014
|
const callee = unwrappedExpression.callee;
|
|
2015
|
-
if (isFunctionLike
|
|
2015
|
+
if (isFunctionLike(callee)) {
|
|
2016
2016
|
markFunctionReturnJsx(callee, programRoot, generatedImageJsxNodes, visitedComponentNames);
|
|
2017
2017
|
return;
|
|
2018
2018
|
}
|
|
@@ -2021,7 +2021,7 @@ const markGeneratedImageExpression = (expression, programRoot, generatedImageJsx
|
|
|
2021
2021
|
if (hasNormalJsxUsage(programRoot, callee.name, generatedImageJsxNodes)) return;
|
|
2022
2022
|
if (hasNormalFunctionCallUsage(programRoot, callee.name)) return;
|
|
2023
2023
|
const binding = findVariableInitializer(callee, callee.name);
|
|
2024
|
-
if (!binding?.initializer || !isFunctionLike
|
|
2024
|
+
if (!binding?.initializer || !isFunctionLike(stripParenExpression(binding.initializer))) return;
|
|
2025
2025
|
visitedComponentNames.add(callee.name);
|
|
2026
2026
|
markFunctionReturnJsx(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
|
|
2027
2027
|
return;
|
|
@@ -4480,7 +4480,7 @@ const getPotentiallyAliasedSymbols = (identifier, scopes) => {
|
|
|
4480
4480
|
const findExecutionBoundary = (node) => {
|
|
4481
4481
|
let current = node;
|
|
4482
4482
|
while (current) {
|
|
4483
|
-
if (isFunctionLike$
|
|
4483
|
+
if (isFunctionLike$1(current) || isNodeOfType(current, "Program")) return current;
|
|
4484
4484
|
current = current.parent ?? null;
|
|
4485
4485
|
}
|
|
4486
4486
|
return null;
|
|
@@ -4509,7 +4509,7 @@ const findDirectCall = (identifier) => {
|
|
|
4509
4509
|
return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
|
|
4510
4510
|
};
|
|
4511
4511
|
const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
|
|
4512
|
-
if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$
|
|
4512
|
+
if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$1(functionNode) || functionNode.generator) return false;
|
|
4513
4513
|
visitedFunctionNodes.add(functionNode);
|
|
4514
4514
|
const referenceBoundary = findExecutionBoundary(referenceNode);
|
|
4515
4515
|
if (!referenceBoundary) return false;
|
|
@@ -4528,7 +4528,7 @@ const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scope
|
|
|
4528
4528
|
const callBoundary = findExecutionBoundary(call);
|
|
4529
4529
|
if (!callBoundary) return false;
|
|
4530
4530
|
if (callBoundary === referenceBoundary) return true;
|
|
4531
|
-
if (!isFunctionLike$
|
|
4531
|
+
if (!isFunctionLike$1(callBoundary)) return false;
|
|
4532
4532
|
return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
|
|
4533
4533
|
});
|
|
4534
4534
|
};
|
|
@@ -4556,7 +4556,7 @@ const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode,
|
|
|
4556
4556
|
const referenceBoundary = findExecutionBoundary(referenceNode);
|
|
4557
4557
|
if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(writeTarget, writeBoundary)) return false;
|
|
4558
4558
|
if (writeBoundary === referenceBoundary) return writeTarget.range[0] < referenceNode.range[0];
|
|
4559
|
-
return isFunctionLike$
|
|
4559
|
+
return isFunctionLike$1(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
|
|
4560
4560
|
});
|
|
4561
4561
|
const isStableStaticPropertyReference = (identifier) => {
|
|
4562
4562
|
if (isDirectAliasSourceReference(identifier)) return true;
|
|
@@ -4597,7 +4597,7 @@ const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, s
|
|
|
4597
4597
|
const findEnclosingFunction$1 = (node) => {
|
|
4598
4598
|
let cursor = node.parent;
|
|
4599
4599
|
while (cursor) {
|
|
4600
|
-
if (isFunctionLike$
|
|
4600
|
+
if (isFunctionLike$1(cursor)) return cursor;
|
|
4601
4601
|
cursor = cursor.parent ?? null;
|
|
4602
4602
|
}
|
|
4603
4603
|
return null;
|
|
@@ -4656,7 +4656,7 @@ const isCommutativeParameterMutation = (statement, parameterNames) => {
|
|
|
4656
4656
|
return isNodeOfType(receiver, "Identifier") && parameterNames.has(receiver.name) && isNodeOfType(stripParenExpression(statement.expression.right), "Literal");
|
|
4657
4657
|
};
|
|
4658
4658
|
const isOrderIndependentFunction = (functionNode, scopes) => {
|
|
4659
|
-
if (!isFunctionLike$
|
|
4659
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
4660
4660
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
4661
4661
|
for (const parameter of functionNode.params) {
|
|
4662
4662
|
if (!isNodeOfType(parameter, "Identifier")) return false;
|
|
@@ -4704,7 +4704,7 @@ const resolveOrderIndependentObjectPropertyFunction = (objectExpression, propert
|
|
|
4704
4704
|
}
|
|
4705
4705
|
if (!matchingProperty || !isNodeOfType(matchingProperty, "Property")) return null;
|
|
4706
4706
|
const propertyValue = stripParenExpression(matchingProperty.value);
|
|
4707
|
-
if (isFunctionLike$
|
|
4707
|
+
if (isFunctionLike$1(propertyValue)) return isOrderIndependentFunction(propertyValue, scopes) ? propertyValue : null;
|
|
4708
4708
|
return resolveOrderIndependentLocalFunction(propertyValue, callExpression, scopes, visitedSymbolIds);
|
|
4709
4709
|
};
|
|
4710
4710
|
const resolveOrderIndependentLocalFunction = (callee, callExpression, scopes, visitedSymbolIds) => {
|
|
@@ -4722,7 +4722,7 @@ const resolveOrderIndependentLocalFunction = (callee, callExpression, scopes, vi
|
|
|
4722
4722
|
const initializer = stripParenExpression(symbol.initializer);
|
|
4723
4723
|
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
4724
4724
|
if (destructuredPropertyName !== null) return resolveOrderIndependentObjectPropertyFunction(initializer, destructuredPropertyName, callExpression, scopes, visitedSymbolIds);
|
|
4725
|
-
if (isFunctionLike$
|
|
4725
|
+
if (isFunctionLike$1(initializer)) return isOrderIndependentFunction(initializer, scopes) ? initializer : null;
|
|
4726
4726
|
if (symbol.kind !== "const") return null;
|
|
4727
4727
|
return resolveOrderIndependentLocalFunction(initializer, callExpression, scopes, visitedSymbolIds);
|
|
4728
4728
|
};
|
|
@@ -4755,7 +4755,7 @@ const findFirstAwaitOutsideNestedFunctions = (block, skipNestedLoops = false) =>
|
|
|
4755
4755
|
let firstAwait = null;
|
|
4756
4756
|
walkAst(block, (child) => {
|
|
4757
4757
|
if (firstAwait) return false;
|
|
4758
|
-
if (child !== block && isFunctionLike$
|
|
4758
|
+
if (child !== block && isFunctionLike$1(child)) return false;
|
|
4759
4759
|
if (skipNestedLoops && child !== block && LOOP_STATEMENT_TYPES.has(child.type)) return false;
|
|
4760
4760
|
if (isNodeOfType(child, "AwaitExpression")) firstAwait = child;
|
|
4761
4761
|
});
|
|
@@ -4835,7 +4835,7 @@ const ARRAY_MUTATION_METHOD_NAMES = new Set([
|
|
|
4835
4835
|
const addDerivedBindings = (block, names) => {
|
|
4836
4836
|
const declaratorBindings = [];
|
|
4837
4837
|
walkAst(block, (child) => {
|
|
4838
|
-
if (child !== block && isFunctionLike$
|
|
4838
|
+
if (child !== block && isFunctionLike$1(child)) return false;
|
|
4839
4839
|
if (!isNodeOfType(child, "VariableDeclarator") || !child.init) return;
|
|
4840
4840
|
if (!isNodeOfType(child.id, "Identifier")) return;
|
|
4841
4841
|
const referencedNames = /* @__PURE__ */ new Set();
|
|
@@ -4862,7 +4862,7 @@ const hasLoopCarriedDependency = (block) => {
|
|
|
4862
4862
|
const carried = /* @__PURE__ */ new Set();
|
|
4863
4863
|
const awaitedReferences = /* @__PURE__ */ new Set();
|
|
4864
4864
|
walkAst(block, (child) => {
|
|
4865
|
-
if (child !== block && isFunctionLike$
|
|
4865
|
+
if (child !== block && isFunctionLike$1(child)) return false;
|
|
4866
4866
|
if (isNodeOfType(child, "AssignmentExpression") && child.left) {
|
|
4867
4867
|
collectPatternIdentifiers(child.left, carried);
|
|
4868
4868
|
return;
|
|
@@ -4884,7 +4884,7 @@ const NESTED_LOOP_OR_SWITCH_TYPES = new Set([...LOOP_TYPES, "SwitchStatement"]);
|
|
|
4884
4884
|
const collectAwaitAssignedBindingNames = (block) => {
|
|
4885
4885
|
const awaitAssignedNames = /* @__PURE__ */ new Set();
|
|
4886
4886
|
walkAst(block, (child) => {
|
|
4887
|
-
if (child !== block && isFunctionLike$
|
|
4887
|
+
if (child !== block && isFunctionLike$1(child)) return false;
|
|
4888
4888
|
if (isNodeOfType(child, "VariableDeclarator") && child.id && containsDirectAwait(child.init)) collectPatternIdentifiers(child.id, awaitAssignedNames);
|
|
4889
4889
|
if (isNodeOfType(child, "AssignmentExpression") && child.left && containsDirectAwait(child.right)) collectPatternIdentifiers(child.left, awaitAssignedNames);
|
|
4890
4890
|
});
|
|
@@ -4954,7 +4954,7 @@ const loopBodyHasAwaitDependentEarlyExit = (block, loopLabelName) => {
|
|
|
4954
4954
|
let hasAwaitDependentExit = false;
|
|
4955
4955
|
walkAst(block, (child) => {
|
|
4956
4956
|
if (hasAwaitDependentExit) return false;
|
|
4957
|
-
if (child !== block && isFunctionLike$
|
|
4957
|
+
if (child !== block && isFunctionLike$1(child)) return false;
|
|
4958
4958
|
if (!(isNodeOfType(child, "ReturnStatement") || isNodeOfType(child, "BreakStatement") && doesBreakExitInspectedLoop(child, block, loopLabelName))) return;
|
|
4959
4959
|
if (isExitAwaitDependent(child, block, awaitAssignedNames)) {
|
|
4960
4960
|
hasAwaitDependentExit = true;
|
|
@@ -4976,7 +4976,7 @@ const MUTATING_ARRAY_METHOD_NAMES$1 = new Set([
|
|
|
4976
4976
|
const collectBodyWrittenBindingNames = (block) => {
|
|
4977
4977
|
const writtenNames = /* @__PURE__ */ new Set();
|
|
4978
4978
|
walkAst(block, (child) => {
|
|
4979
|
-
if (child !== block && isFunctionLike$
|
|
4979
|
+
if (child !== block && isFunctionLike$1(child)) return false;
|
|
4980
4980
|
if (isNodeOfType(child, "AssignmentExpression") && child.left) {
|
|
4981
4981
|
collectPatternIdentifiers(child.left, writtenNames);
|
|
4982
4982
|
const assignedRootName = getRootObjectIdentifierName(child.left);
|
|
@@ -5072,7 +5072,7 @@ const resolvePromiseFlowNode = (mapCall) => {
|
|
|
5072
5072
|
};
|
|
5073
5073
|
const findEnclosingFunctionOrProgram = (node) => {
|
|
5074
5074
|
let current = node;
|
|
5075
|
-
while (current.parent && !isFunctionLike$
|
|
5075
|
+
while (current.parent && !isFunctionLike$1(current)) current = current.parent;
|
|
5076
5076
|
return current;
|
|
5077
5077
|
};
|
|
5078
5078
|
const isBindingCombinedWithPromiseConcurrency = (bindingName, scope) => {
|
|
@@ -5101,7 +5101,7 @@ const isWrappedInPromiseConcurrency = (mapCall) => {
|
|
|
5101
5101
|
};
|
|
5102
5102
|
const isLoopInsideWorkerPoolFunction = (loopNode) => {
|
|
5103
5103
|
const enclosingFunction = findEnclosingFunctionOrProgram(loopNode);
|
|
5104
|
-
if (!isFunctionLike$
|
|
5104
|
+
if (!isFunctionLike$1(enclosingFunction)) return false;
|
|
5105
5105
|
let functionBindingName = null;
|
|
5106
5106
|
if (isNodeOfType(enclosingFunction, "FunctionDeclaration") && isNodeOfType(enclosingFunction.id, "Identifier")) functionBindingName = enclosingFunction.id.name;
|
|
5107
5107
|
else if (isNodeOfType(enclosingFunction.parent, "VariableDeclarator") && isNodeOfType(enclosingFunction.parent.id, "Identifier")) functionBindingName = enclosingFunction.parent.id.name;
|
|
@@ -5369,7 +5369,7 @@ const guardConsequentPerformsSideEffects = (consequent) => {
|
|
|
5369
5369
|
let performsSideEffects = false;
|
|
5370
5370
|
walkAst(consequent, (child) => {
|
|
5371
5371
|
if (performsSideEffects) return false;
|
|
5372
|
-
if (isFunctionLike$
|
|
5372
|
+
if (isFunctionLike$1(child)) return false;
|
|
5373
5373
|
if (isNodeOfType(child, "ThrowStatement")) return false;
|
|
5374
5374
|
if (isNodeOfType(child, "CallExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression")) {
|
|
5375
5375
|
performsSideEffects = true;
|
|
@@ -5381,7 +5381,7 @@ const guardConsequentPerformsSideEffects = (consequent) => {
|
|
|
5381
5381
|
const findEnclosingFunction = (node) => {
|
|
5382
5382
|
let ancestor = node.parent;
|
|
5383
5383
|
while (ancestor) {
|
|
5384
|
-
if (isFunctionLike$
|
|
5384
|
+
if (isFunctionLike$1(ancestor)) return ancestor;
|
|
5385
5385
|
ancestor = ancestor.parent;
|
|
5386
5386
|
}
|
|
5387
5387
|
return null;
|
|
@@ -5392,7 +5392,7 @@ const guardTestReadsReassignedLocal = (test, guardStatement) => {
|
|
|
5392
5392
|
collectReferenceIdentifierNames(test, testIdentifierNames);
|
|
5393
5393
|
if (testIdentifierNames.size === 0) return false;
|
|
5394
5394
|
const enclosingFunction = findEnclosingFunction(guardStatement);
|
|
5395
|
-
if (!enclosingFunction || !isFunctionLike$
|
|
5395
|
+
if (!enclosingFunction || !isFunctionLike$1(enclosingFunction) || !enclosingFunction.body) return false;
|
|
5396
5396
|
let readsReassignedLocal = false;
|
|
5397
5397
|
walkAst(enclosingFunction.body, (child) => {
|
|
5398
5398
|
if (readsReassignedLocal) return false;
|
|
@@ -5487,13 +5487,13 @@ const asyncDeferAwait = defineRule({
|
|
|
5487
5487
|
const inspectAllStatementBlocks = (functionBody) => {
|
|
5488
5488
|
if (!functionBody) return;
|
|
5489
5489
|
walkAst(functionBody, (descendant) => {
|
|
5490
|
-
if (isFunctionLike$
|
|
5490
|
+
if (isFunctionLike$1(descendant)) return false;
|
|
5491
5491
|
if (isNodeOfType(descendant, "BlockStatement")) inspectStatements(descendant.body ?? []);
|
|
5492
5492
|
else if (isNodeOfType(descendant, "SwitchCase")) inspectStatements(descendant.consequent ?? []);
|
|
5493
5493
|
});
|
|
5494
5494
|
};
|
|
5495
5495
|
const enterFunction = (node) => {
|
|
5496
|
-
if (!isFunctionLike$
|
|
5496
|
+
if (!isFunctionLike$1(node)) return;
|
|
5497
5497
|
if (!node.async) return;
|
|
5498
5498
|
if (!isNodeOfType(node.body, "BlockStatement")) return;
|
|
5499
5499
|
inspectAllStatementBlocks(node.body);
|
|
@@ -5598,7 +5598,7 @@ const sequenceContainsSerializationSignal = (statements, context) => {
|
|
|
5598
5598
|
const isInsideTransactionCallback = (node) => {
|
|
5599
5599
|
let current = node.parent;
|
|
5600
5600
|
while (current) {
|
|
5601
|
-
if (isFunctionLike$
|
|
5601
|
+
if (isFunctionLike$1(current)) {
|
|
5602
5602
|
const callParent = current.parent;
|
|
5603
5603
|
if (callParent && isNodeOfType(callParent, "CallExpression")) {
|
|
5604
5604
|
const callee = callParent.callee;
|
|
@@ -6937,7 +6937,7 @@ const catchReturnsFallback = (tryStatement) => {
|
|
|
6937
6937
|
let hasReturn = false;
|
|
6938
6938
|
let hasThrow = false;
|
|
6939
6939
|
walkAst(handler.body, (child) => {
|
|
6940
|
-
if (isFunctionLike$
|
|
6940
|
+
if (isFunctionLike$1(child)) return false;
|
|
6941
6941
|
if (isNodeOfType(child, "ReturnStatement")) hasReturn = true;
|
|
6942
6942
|
if (isNodeOfType(child, "ThrowStatement")) hasThrow = true;
|
|
6943
6943
|
});
|
|
@@ -6949,10 +6949,10 @@ const resolveValidatorFunction = (callee, scopes) => {
|
|
|
6949
6949
|
const symbol = scopes.symbolFor(unwrappedCallee);
|
|
6950
6950
|
if (!symbol) return null;
|
|
6951
6951
|
const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
|
|
6952
|
-
return isFunctionLike$
|
|
6952
|
+
return isFunctionLike$1(candidate) ? candidate : null;
|
|
6953
6953
|
};
|
|
6954
6954
|
const validatorChecksPayloadProperties = (validatorFunction, scopes) => {
|
|
6955
|
-
if (!isFunctionLike$
|
|
6955
|
+
if (!isFunctionLike$1(validatorFunction)) return false;
|
|
6956
6956
|
const firstParameter = validatorFunction.params?.[0];
|
|
6957
6957
|
if (!firstParameter || !isNodeOfType(firstParameter, "Identifier")) return false;
|
|
6958
6958
|
const parameterSymbol = scopes.symbolFor(firstParameter);
|
|
@@ -6960,7 +6960,7 @@ const validatorChecksPayloadProperties = (validatorFunction, scopes) => {
|
|
|
6960
6960
|
const payloadSymbolIds = new Set([parameterSymbol.id]);
|
|
6961
6961
|
let hasPropertyTypeCheck = false;
|
|
6962
6962
|
walkAst(validatorFunction.body, (child) => {
|
|
6963
|
-
if (isFunctionLike$
|
|
6963
|
+
if (isFunctionLike$1(child)) return false;
|
|
6964
6964
|
if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && child.init) {
|
|
6965
6965
|
const initializer = stripParenExpression(child.init);
|
|
6966
6966
|
if (isNodeOfType(initializer, "Identifier")) {
|
|
@@ -7004,7 +7004,7 @@ const collectSafelyValidatedLocalStorageKeys = ({ programNode, scopes, resolveKe
|
|
|
7004
7004
|
const rawValueKeys = /* @__PURE__ */ new Map();
|
|
7005
7005
|
const parsedValueSources = /* @__PURE__ */ new Map();
|
|
7006
7006
|
walkAst(child.block, (tryChild) => {
|
|
7007
|
-
if (isFunctionLike$
|
|
7007
|
+
if (isFunctionLike$1(tryChild)) return false;
|
|
7008
7008
|
if (isNodeOfType(tryChild, "VariableDeclarator") && isNodeOfType(tryChild.id, "Identifier") && tryChild.init) {
|
|
7009
7009
|
const initializer = stripParenExpression(tryChild.init);
|
|
7010
7010
|
if (isNodeOfType(initializer, "CallExpression") && isLocalStorageCall(initializer, "getItem")) {
|
|
@@ -7144,7 +7144,7 @@ const resolveExpressionKey$1 = (expression, context, visitedSymbolIds = /* @__PU
|
|
|
7144
7144
|
}
|
|
7145
7145
|
if (isNodeOfType(unwrappedExpression, "ThisExpression")) return "this";
|
|
7146
7146
|
if (isNodeOfType(unwrappedExpression, "Literal") && (typeof unwrappedExpression.value === "string" || typeof unwrappedExpression.value === "number")) return `literal:${String(unwrappedExpression.value)}`;
|
|
7147
|
-
if (isFunctionLike$
|
|
7147
|
+
if (isFunctionLike$1(unwrappedExpression)) {
|
|
7148
7148
|
const rangeStart = getRangeStart(unwrappedExpression);
|
|
7149
7149
|
return rangeStart === null ? null : `function:${rangeStart}`;
|
|
7150
7150
|
}
|
|
@@ -7306,15 +7306,15 @@ const getLocalCalledFunction = (callExpression, context) => {
|
|
|
7306
7306
|
if (!isNodeOfType(callee, "Identifier")) return null;
|
|
7307
7307
|
const symbol = context.scopes.symbolFor(callee);
|
|
7308
7308
|
const candidate = symbol?.initializer ?? symbol?.declarationNode;
|
|
7309
|
-
return candidate && isFunctionLike$
|
|
7309
|
+
return candidate && isFunctionLike$1(stripParenExpression(candidate)) ? stripParenExpression(candidate) : null;
|
|
7310
7310
|
};
|
|
7311
7311
|
const hasDominatingEscapeMutation = (symbol, callNode, context, mutationIndex) => getSymbolEscapeCalls(symbol, context).some((escape) => escape.callExpression !== callNode && mutationDominatesCall(escape.callExpression, callNode, context) && callMayMutateArgument(escape.callExpression, escape.argumentIndex, context, mutationIndex, /* @__PURE__ */ new Map()));
|
|
7312
7312
|
const factoryReturnsMutatedReceiver = (receiver, methodName, context, mutationIndex) => {
|
|
7313
7313
|
const factory = getLocalCalledFunction(stripParenExpression(receiver), context);
|
|
7314
|
-
if (!factory || !isFunctionLike$
|
|
7314
|
+
if (!factory || !isFunctionLike$1(factory)) return false;
|
|
7315
7315
|
const returnStatements = [];
|
|
7316
7316
|
walkAst(factory.body, (node) => {
|
|
7317
|
-
if (node !== factory.body && isFunctionLike$
|
|
7317
|
+
if (node !== factory.body && isFunctionLike$1(node)) return false;
|
|
7318
7318
|
if (isNodeOfType(node, "ReturnStatement") && node.argument) returnStatements.push(node);
|
|
7319
7319
|
});
|
|
7320
7320
|
return returnStatements.length > 0 && returnStatements.every((returnStatement) => {
|
|
@@ -7395,11 +7395,11 @@ const getSymbolEscapeCalls = (symbol, context, visitedSymbols = /* @__PURE__ */
|
|
|
7395
7395
|
return result;
|
|
7396
7396
|
};
|
|
7397
7397
|
const functionReturnsParameter = (functionNode, parameterIdentity, context) => {
|
|
7398
|
-
if (!isFunctionLike$
|
|
7398
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
7399
7399
|
if (isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode.body, "BlockStatement")) return resolveExpressionKey$1(functionNode.body, context) === parameterIdentity;
|
|
7400
7400
|
const returnStatements = [];
|
|
7401
7401
|
walkAst(functionNode.body, (node) => {
|
|
7402
|
-
if (node !== functionNode.body && isFunctionLike$
|
|
7402
|
+
if (node !== functionNode.body && isFunctionLike$1(node)) return false;
|
|
7403
7403
|
if (isNodeOfType(node, "ReturnStatement")) returnStatements.push(node);
|
|
7404
7404
|
});
|
|
7405
7405
|
return returnStatements.length > 0 && returnStatements.every((returnStatement) => isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument && resolveExpressionKey$1(returnStatement.argument, context) === parameterIdentity);
|
|
@@ -7419,7 +7419,7 @@ const getCallResultSymbol = (callExpression, context) => {
|
|
|
7419
7419
|
};
|
|
7420
7420
|
const callMayMutateArgument = (callExpression, argumentIndex, context, mutationIndex, visitedParameterIndexesByFunction) => {
|
|
7421
7421
|
const calledFunction = getLocalCalledFunction(callExpression, context);
|
|
7422
|
-
if (!calledFunction || !isFunctionLike$
|
|
7422
|
+
if (!calledFunction || !isFunctionLike$1(calledFunction)) return true;
|
|
7423
7423
|
if (calledFunction.generator) return false;
|
|
7424
7424
|
const visitedParameterIndexes = visitedParameterIndexesByFunction.get(calledFunction) ?? /* @__PURE__ */ new Set();
|
|
7425
7425
|
if (visitedParameterIndexes.has(argumentIndex)) return false;
|
|
@@ -7496,10 +7496,10 @@ const createMethodMutationAnalysis = (context) => {
|
|
|
7496
7496
|
//#endregion
|
|
7497
7497
|
//#region src/plugin/utils/collect-function-return-statements.ts
|
|
7498
7498
|
const collectFunctionReturnStatements = (functionNode) => {
|
|
7499
|
-
if (!isFunctionLike$
|
|
7499
|
+
if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return [];
|
|
7500
7500
|
const returnStatements = [];
|
|
7501
7501
|
walkAst(functionNode.body, (node) => {
|
|
7502
|
-
if (node !== functionNode.body && (isFunctionLike$
|
|
7502
|
+
if (node !== functionNode.body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
|
|
7503
7503
|
if (isNodeOfType(node, "ReturnStatement")) returnStatements.push(node);
|
|
7504
7504
|
});
|
|
7505
7505
|
return returnStatements;
|
|
@@ -7517,7 +7517,7 @@ const statementAlwaysExits = (statement) => {
|
|
|
7517
7517
|
const REASSIGNABLE_BINDING_KINDS = new Set(["let", "var"]);
|
|
7518
7518
|
const CONDITIONAL_EXPRESSION_TYPES = new Set(["ConditionalExpression", "LogicalExpression"]);
|
|
7519
7519
|
const collectReturnedExpressions = (functionNode) => {
|
|
7520
|
-
if (!isFunctionLike$
|
|
7520
|
+
if (!isFunctionLike$1(functionNode) || !functionNode.body) return [];
|
|
7521
7521
|
if (!isNodeOfType(functionNode.body, "BlockStatement")) return [functionNode.body];
|
|
7522
7522
|
return collectFunctionReturnStatements(functionNode).flatMap((returnStatement) => returnStatement.argument ? [returnStatement.argument] : []);
|
|
7523
7523
|
};
|
|
@@ -7619,11 +7619,11 @@ const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow)
|
|
|
7619
7619
|
return [...applyDefinitions(incomingDefinitionsByBlock.get(referenceBlock) ?? /* @__PURE__ */ new Set(), definitionsBeforeReference)].map((definition) => definition.expression);
|
|
7620
7620
|
};
|
|
7621
7621
|
const functionHasBareReturn = (functionNode) => {
|
|
7622
|
-
if (!isFunctionLike$
|
|
7622
|
+
if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return false;
|
|
7623
7623
|
let didFindBareReturn = false;
|
|
7624
7624
|
walkAst(functionNode.body, (node) => {
|
|
7625
7625
|
if (didFindBareReturn) return false;
|
|
7626
|
-
if (node !== functionNode.body && isFunctionLike$
|
|
7626
|
+
if (node !== functionNode.body && isFunctionLike$1(node)) return false;
|
|
7627
7627
|
if (isNodeOfType(node, "ReturnStatement") && !node.argument) didFindBareReturn = true;
|
|
7628
7628
|
});
|
|
7629
7629
|
return didFindBareReturn;
|
|
@@ -7635,7 +7635,7 @@ const functionReturnsMatchingExpression = (functionNode, scopes, matchesExpressi
|
|
|
7635
7635
|
if (visitedFunctions.has(candidateFunction)) return false;
|
|
7636
7636
|
visitedFunctions.add(candidateFunction);
|
|
7637
7637
|
const returnedExpressions = collectReturnedExpressions(candidateFunction);
|
|
7638
|
-
if (matchMode === "every" && isFunctionLike$
|
|
7638
|
+
if (matchMode === "every" && isFunctionLike$1(candidateFunction) && isNodeOfType(candidateFunction.body, "BlockStatement") && (!statementAlwaysExits(candidateFunction.body) || functionHasBareReturn(candidateFunction))) return false;
|
|
7639
7639
|
return returnedExpressions.length > 0 && (matchMode === "every" ? returnedExpressions.every(expressionMatches) : returnedExpressions.some(expressionMatches));
|
|
7640
7640
|
};
|
|
7641
7641
|
const expressionMatches = (expression) => {
|
|
@@ -7648,7 +7648,7 @@ const functionReturnsMatchingExpression = (functionNode, scopes, matchesExpressi
|
|
|
7648
7648
|
if (!symbol || symbol.kind !== "const" && !REASSIGNABLE_BINDING_KINDS.has(symbol.kind)) return false;
|
|
7649
7649
|
return collectPossibleAssignedExpressions(symbol, unwrappedExpression, controlFlow).some((assignedExpression) => {
|
|
7650
7650
|
const assignedValue = stripParenExpression(assignedExpression);
|
|
7651
|
-
return !isFunctionLike$
|
|
7651
|
+
return !isFunctionLike$1(assignedValue) && expressionMatches(assignedValue);
|
|
7652
7652
|
});
|
|
7653
7653
|
}
|
|
7654
7654
|
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
@@ -7657,7 +7657,7 @@ const functionReturnsMatchingExpression = (functionNode, scopes, matchesExpressi
|
|
|
7657
7657
|
const symbol = scopes.symbolFor(unwrappedExpression.callee);
|
|
7658
7658
|
if (!symbol || symbol.kind !== "const" && symbol.kind !== "function") return false;
|
|
7659
7659
|
const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
|
|
7660
|
-
const candidateFunction = isFunctionLike$
|
|
7660
|
+
const candidateFunction = isFunctionLike$1(initializer) ? initializer : isFunctionLike$1(symbol.declarationNode) ? symbol.declarationNode : null;
|
|
7661
7661
|
if (!candidateFunction || candidateFunction.async || candidateFunction.generator || candidateFunction.params.length !== 0) return false;
|
|
7662
7662
|
return functionMatches(candidateFunction);
|
|
7663
7663
|
}
|
|
@@ -7988,7 +7988,7 @@ const getSameFileCalledFunction = (callExpression, scopes, visitedSymbolIds) =>
|
|
|
7988
7988
|
if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
|
|
7989
7989
|
if (symbol.kind !== "const" && !(symbol.kind === "function" && symbol.references.every((reference) => reference.flag === "read"))) return null;
|
|
7990
7990
|
const candidate = symbol.initializer ?? symbol.declarationNode;
|
|
7991
|
-
if (!isFunctionLike$
|
|
7991
|
+
if (!isFunctionLike$1(candidate) || candidate.async || candidate.generator) return null;
|
|
7992
7992
|
visitedSymbolIds.add(symbol.id);
|
|
7993
7993
|
return candidate;
|
|
7994
7994
|
};
|
|
@@ -8071,12 +8071,8 @@ const isProvenBrowserApiReceiver = (receiver, receiverKind, scopes, visitedSymbo
|
|
|
8071
8071
|
const GENERATED_DOCS_ARCHIVE_PATH_PATTERN = /(?:^|\/)(?:docs?|documentation)\/archive\/[^/]+\/static\/(?:[^/]+\/)*docs\.js$/i;
|
|
8072
8072
|
const isGeneratedDocsArchiveFilename = (filename) => GENERATED_DOCS_ARCHIVE_PATH_PATTERN.test(normalizeFilename(filename ?? ""));
|
|
8073
8073
|
//#endregion
|
|
8074
|
-
//#region src/plugin/utils/find-exported-
|
|
8075
|
-
const
|
|
8076
|
-
if (!node) return false;
|
|
8077
|
-
return isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ArrowFunctionExpression");
|
|
8078
|
-
};
|
|
8079
|
-
const findExportedFunctionBody = (programRoot, exportedName) => {
|
|
8074
|
+
//#region src/plugin/utils/find-exported-value.ts
|
|
8075
|
+
const findExportedValue = (programRoot, exportedName) => {
|
|
8080
8076
|
if (!isNodeOfType(programRoot, "Program")) return null;
|
|
8081
8077
|
const localBindings = /* @__PURE__ */ new Map();
|
|
8082
8078
|
const namedExports = /* @__PURE__ */ new Map();
|
|
@@ -8085,9 +8081,8 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
|
|
|
8085
8081
|
const recordVariableDeclaration = (declaration) => {
|
|
8086
8082
|
for (const declarator of declaration.declarations ?? []) {
|
|
8087
8083
|
if (!isNodeOfType(declarator, "VariableDeclarator")) continue;
|
|
8088
|
-
if (!isNodeOfType(declarator.id, "Identifier")) continue;
|
|
8089
|
-
|
|
8090
|
-
if (initializer && isFunctionLike(initializer)) localBindings.set(declarator.id.name, initializer);
|
|
8084
|
+
if (!isNodeOfType(declarator.id, "Identifier") || !declarator.init) continue;
|
|
8085
|
+
localBindings.set(declarator.id.name, stripParenExpression(declarator.init));
|
|
8091
8086
|
}
|
|
8092
8087
|
};
|
|
8093
8088
|
for (const statement of programRoot.body ?? []) {
|
|
@@ -8114,33 +8109,22 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
|
|
|
8114
8109
|
namedExports.set(declaration.id.name, declaration.id.name);
|
|
8115
8110
|
}
|
|
8116
8111
|
for (const specifier of statement.specifiers ?? []) {
|
|
8117
|
-
if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
if (
|
|
8122
|
-
|
|
8123
|
-
if (!exportedNameSpec) continue;
|
|
8124
|
-
namedExports.set(exportedNameSpec, local.name);
|
|
8112
|
+
if (!isNodeOfType(specifier, "ExportSpecifier") || specifier.exportKind === "type") continue;
|
|
8113
|
+
const localName = isNodeOfType(specifier.local, "Identifier") ? specifier.local.name : null;
|
|
8114
|
+
let exportedSpecifierName = null;
|
|
8115
|
+
if (isNodeOfType(specifier.exported, "Identifier")) exportedSpecifierName = specifier.exported.name;
|
|
8116
|
+
else if (isNodeOfType(specifier.exported, "Literal") && typeof specifier.exported.value === "string") exportedSpecifierName = specifier.exported.value;
|
|
8117
|
+
if (localName && exportedSpecifierName) namedExports.set(exportedSpecifierName, localName);
|
|
8125
8118
|
}
|
|
8126
8119
|
continue;
|
|
8127
8120
|
}
|
|
8128
|
-
if (isNodeOfType(statement, "ExportDefaultDeclaration"))
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
}
|
|
8136
|
-
if (isFunctionLike(declaration)) {
|
|
8137
|
-
defaultExport = declaration;
|
|
8138
|
-
continue;
|
|
8139
|
-
}
|
|
8140
|
-
if (isNodeOfType(declaration, "Identifier")) {
|
|
8141
|
-
defaultExportIdentifierName = declaration.name;
|
|
8142
|
-
continue;
|
|
8143
|
-
}
|
|
8121
|
+
if (!isNodeOfType(statement, "ExportDefaultDeclaration")) continue;
|
|
8122
|
+
const declaration = statement.declaration;
|
|
8123
|
+
if (!declaration) continue;
|
|
8124
|
+
if (isNodeOfType(declaration, "Identifier")) defaultExportIdentifierName = declaration.name;
|
|
8125
|
+
else {
|
|
8126
|
+
if ((isNodeOfType(declaration, "FunctionDeclaration") || isNodeOfType(declaration, "ClassDeclaration")) && declaration.id) localBindings.set(declaration.id.name, declaration);
|
|
8127
|
+
defaultExport = stripParenExpression(declaration);
|
|
8144
8128
|
}
|
|
8145
8129
|
}
|
|
8146
8130
|
if (exportedName === "default") {
|
|
@@ -8151,9 +8135,10 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
|
|
|
8151
8135
|
}
|
|
8152
8136
|
}
|
|
8153
8137
|
const localName = namedExports.get(exportedName);
|
|
8154
|
-
|
|
8155
|
-
return localBindings.get(localName) ?? null;
|
|
8138
|
+
return localName ? localBindings.get(localName) ?? null : null;
|
|
8156
8139
|
};
|
|
8140
|
+
//#endregion
|
|
8141
|
+
//#region src/plugin/utils/find-exported-function-body.ts
|
|
8157
8142
|
const resolveImportedExportName = (importSpecifier) => {
|
|
8158
8143
|
if (isNodeOfType(importSpecifier, "ImportSpecifier")) {
|
|
8159
8144
|
const imported = importSpecifier.imported;
|
|
@@ -8610,28 +8595,44 @@ const resolveModulePath = (fromFilename, source) => {
|
|
|
8610
8595
|
};
|
|
8611
8596
|
//#endregion
|
|
8612
8597
|
//#region src/plugin/utils/resolve-cross-file-function-export.ts
|
|
8613
|
-
const
|
|
8598
|
+
const resolveValueExportInFile = (filePath, exportedName, visitedFilePaths) => {
|
|
8614
8599
|
if (visitedFilePaths.size >= 4) return null;
|
|
8615
8600
|
if (visitedFilePaths.has(filePath)) return null;
|
|
8616
8601
|
visitedFilePaths.add(filePath);
|
|
8617
8602
|
const programRoot = parseSourceFile(filePath);
|
|
8618
8603
|
if (!programRoot) return null;
|
|
8619
|
-
const exported =
|
|
8620
|
-
if (exported) return
|
|
8621
|
-
|
|
8604
|
+
const exported = findExportedValue(programRoot, exportedName);
|
|
8605
|
+
if (exported) return {
|
|
8606
|
+
filePath,
|
|
8607
|
+
exportedNode: exported,
|
|
8608
|
+
programNode: programRoot
|
|
8609
|
+
};
|
|
8610
|
+
const resolvedCandidates = /* @__PURE__ */ new Map();
|
|
8622
8611
|
for (const target of findReExportTargetsForName(programRoot, exportedName)) {
|
|
8623
8612
|
const nextFilePath = resolveModulePath(filePath, target.source);
|
|
8624
8613
|
if (!nextFilePath) continue;
|
|
8625
|
-
const resolved =
|
|
8626
|
-
if (resolved) resolvedCandidates.
|
|
8614
|
+
const resolved = resolveValueExportInFile(nextFilePath, target.importedName, new Set(visitedFilePaths));
|
|
8615
|
+
if (resolved) resolvedCandidates.set(resolved.exportedNode, resolved);
|
|
8627
8616
|
}
|
|
8628
8617
|
if (resolvedCandidates.size !== 1) return null;
|
|
8629
8618
|
return resolvedCandidates.values().next().value ?? null;
|
|
8630
8619
|
};
|
|
8631
8620
|
const resolveCrossFileFunctionExport = (fromFilename, source, exportedName) => {
|
|
8621
|
+
return resolveCrossFileFunctionExportWithFilePath(fromFilename, source, exportedName)?.functionNode ?? null;
|
|
8622
|
+
};
|
|
8623
|
+
const resolveCrossFileFunctionExportWithFilePath = (fromFilename, source, exportedName) => {
|
|
8624
|
+
const resolved = resolveCrossFileValueExportWithFilePath(fromFilename, source, exportedName);
|
|
8625
|
+
if (!resolved || !isFunctionLike$1(resolved.exportedNode)) return null;
|
|
8626
|
+
return {
|
|
8627
|
+
filePath: resolved.filePath,
|
|
8628
|
+
functionNode: resolved.exportedNode,
|
|
8629
|
+
programNode: resolved.programNode
|
|
8630
|
+
};
|
|
8631
|
+
};
|
|
8632
|
+
const resolveCrossFileValueExportWithFilePath = (fromFilename, source, exportedName) => {
|
|
8632
8633
|
const resolvedFilePath = resolveModulePath(fromFilename, source);
|
|
8633
8634
|
if (!resolvedFilePath) return null;
|
|
8634
|
-
return
|
|
8635
|
+
return resolveValueExportInFile(resolvedFilePath, exportedName, /* @__PURE__ */ new Set());
|
|
8635
8636
|
};
|
|
8636
8637
|
//#endregion
|
|
8637
8638
|
//#region src/plugin/utils/is-proven-pure-imported-predicate-call.ts
|
|
@@ -8682,7 +8683,7 @@ const isProvenPureImportedPredicateCall = (callExpression, eventArgumentIndex, c
|
|
|
8682
8683
|
const exportedName = resolveImportedExportName(importSpecifier);
|
|
8683
8684
|
if (!exportedName || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string") return false;
|
|
8684
8685
|
const importedFunction = resolveCrossFileFunctionExport(context.filename, importDeclaration.source.value, exportedName);
|
|
8685
|
-
if (!importedFunction || !isFunctionLike$
|
|
8686
|
+
if (!importedFunction || !isFunctionLike$1(importedFunction)) return false;
|
|
8686
8687
|
const parameter = importedFunction.params[eventArgumentIndex];
|
|
8687
8688
|
if (!isNodeOfType(parameter, "Identifier")) return false;
|
|
8688
8689
|
const aliasNames = new Set([parameter.name]);
|
|
@@ -8736,11 +8737,11 @@ const PROMISE_DEFERRED_METHOD_NAMES = new Set([
|
|
|
8736
8737
|
]);
|
|
8737
8738
|
const EMPTY_VISITORS$7 = {};
|
|
8738
8739
|
const handlerCallsPreventDefault = (handler) => {
|
|
8739
|
-
if (!isFunctionLike$
|
|
8740
|
+
if (!isFunctionLike$1(handler)) return false;
|
|
8740
8741
|
let didFindPreventDefault = false;
|
|
8741
8742
|
walkAst(handler, (child) => {
|
|
8742
8743
|
if (didFindPreventDefault) return false;
|
|
8743
|
-
if (child !== handler && isFunctionLike$
|
|
8744
|
+
if (child !== handler && isFunctionLike$1(child)) return false;
|
|
8744
8745
|
if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "MemberExpression") && getStaticPropertyName(child.callee) === "preventDefault") didFindPreventDefault = true;
|
|
8745
8746
|
});
|
|
8746
8747
|
return didFindPreventDefault;
|
|
@@ -8771,18 +8772,18 @@ const handlerArgumentCallsPreventDefault = (handler) => {
|
|
|
8771
8772
|
const resolveHandlerFunction = (handler, context) => {
|
|
8772
8773
|
if (!handler) return void 0;
|
|
8773
8774
|
const unwrappedHandler = stripParenExpression(handler);
|
|
8774
|
-
if (isFunctionLike$
|
|
8775
|
+
if (isFunctionLike$1(unwrappedHandler)) return unwrappedHandler;
|
|
8775
8776
|
if (isNodeOfType(unwrappedHandler, "Identifier")) {
|
|
8776
8777
|
const symbol = context.scopes.symbolFor(unwrappedHandler);
|
|
8777
8778
|
const candidate = symbol?.initializer ?? symbol?.declarationNode;
|
|
8778
|
-
if (candidate && isFunctionLike$
|
|
8779
|
+
if (candidate && isFunctionLike$1(candidate)) return candidate;
|
|
8779
8780
|
const binding = findVariableInitializer(unwrappedHandler, unwrappedHandler.name);
|
|
8780
8781
|
let assignedFunction;
|
|
8781
8782
|
if (symbol && binding) walkAst(binding.scopeOwner, (child) => {
|
|
8782
8783
|
if (child.range[0] >= unwrappedHandler.range[0]) return false;
|
|
8783
8784
|
if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && isNodeOfType(child.left, "Identifier") && context.scopes.symbolFor(child.left) === symbol) {
|
|
8784
8785
|
const assignedValue = stripParenExpression(child.right);
|
|
8785
|
-
assignedFunction = isFunctionLike$
|
|
8786
|
+
assignedFunction = isFunctionLike$1(assignedValue) ? assignedValue : void 0;
|
|
8786
8787
|
}
|
|
8787
8788
|
});
|
|
8788
8789
|
return assignedFunction;
|
|
@@ -8813,7 +8814,7 @@ const handlerMayExposeEvent = (handler, context) => {
|
|
|
8813
8814
|
const visitedParameterIndexesByFunction = /* @__PURE__ */ new Map();
|
|
8814
8815
|
const isAsyncFunctionReference = (rawExpression, visitedSymbolIds) => {
|
|
8815
8816
|
const expression = stripParenExpression(rawExpression);
|
|
8816
|
-
if (isFunctionLike$
|
|
8817
|
+
if (isFunctionLike$1(expression)) return expression.async;
|
|
8817
8818
|
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
8818
8819
|
const symbol = context.scopes.symbolFor(expression);
|
|
8819
8820
|
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
@@ -8883,7 +8884,7 @@ const handlerMayExposeEvent = (handler, context) => {
|
|
|
8883
8884
|
return true;
|
|
8884
8885
|
};
|
|
8885
8886
|
const functionMayExposeParameter = (functionNode, parameterIndex, isRootHandler = false) => {
|
|
8886
|
-
if (!isFunctionLike$
|
|
8887
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
8887
8888
|
const visitedParameterIndexes = visitedParameterIndexesByFunction.get(functionNode) ?? /* @__PURE__ */ new Set();
|
|
8888
8889
|
if (visitedParameterIndexes.has(parameterIndex)) return false;
|
|
8889
8890
|
visitedParameterIndexes.add(parameterIndex);
|
|
@@ -8970,7 +8971,7 @@ const handlerMayExposeEvent = (handler, context) => {
|
|
|
8970
8971
|
let didExposeEvent = false;
|
|
8971
8972
|
walkAst(functionNode.body, (child) => {
|
|
8972
8973
|
if (didExposeEvent) return false;
|
|
8973
|
-
if (child !== functionNode.body && isFunctionLike$
|
|
8974
|
+
if (child !== functionNode.body && isFunctionLike$1(child) && !shouldVisitNestedFunction(child)) return false;
|
|
8974
8975
|
if (isNodeOfType(child, "VariableDeclarator") && child.init) {
|
|
8975
8976
|
addAliasesFromPattern(child.id, child.init);
|
|
8976
8977
|
return;
|
|
@@ -10988,7 +10989,7 @@ const resolveAliasedSymbol = (identifier, context, visitedSymbolIds) => {
|
|
|
10988
10989
|
const resolveCallbackIdentity = (callbackNode, context) => {
|
|
10989
10990
|
if (!callbackNode) return null;
|
|
10990
10991
|
const unwrappedCallback = stripParenExpression(callbackNode);
|
|
10991
|
-
if (isFunctionLike$
|
|
10992
|
+
if (isFunctionLike$1(unwrappedCallback)) return {
|
|
10992
10993
|
node: unwrappedCallback,
|
|
10993
10994
|
isConcreteFunction: true
|
|
10994
10995
|
};
|
|
@@ -10996,7 +10997,7 @@ const resolveCallbackIdentity = (callbackNode, context) => {
|
|
|
10996
10997
|
const symbol = resolveAliasedSymbol(unwrappedCallback, context, /* @__PURE__ */ new Set());
|
|
10997
10998
|
if (!symbol) return null;
|
|
10998
10999
|
const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
|
|
10999
|
-
if (initializer && isFunctionLike$
|
|
11000
|
+
if (initializer && isFunctionLike$1(initializer)) return {
|
|
11000
11001
|
node: initializer,
|
|
11001
11002
|
isConcreteFunction: true
|
|
11002
11003
|
};
|
|
@@ -11226,12 +11227,12 @@ const readDestructuredRemovalCandidate = (node, context) => {
|
|
|
11226
11227
|
const resolveReturnedCleanupBody = (returnedValue, context) => {
|
|
11227
11228
|
if (!returnedValue) return null;
|
|
11228
11229
|
const unwrappedValue = stripParenExpression(returnedValue);
|
|
11229
|
-
if (isFunctionLike$
|
|
11230
|
+
if (isFunctionLike$1(unwrappedValue)) return unwrappedValue.body;
|
|
11230
11231
|
if (!isNodeOfType(unwrappedValue, "Identifier")) return null;
|
|
11231
11232
|
const symbol = resolveAliasedSymbol(unwrappedValue, context, /* @__PURE__ */ new Set());
|
|
11232
11233
|
if (!symbol || !symbol.initializer) return null;
|
|
11233
11234
|
const initializer = stripParenExpression(symbol.initializer);
|
|
11234
|
-
return isFunctionLike$
|
|
11235
|
+
return isFunctionLike$1(initializer) ? initializer.body : null;
|
|
11235
11236
|
};
|
|
11236
11237
|
const collectEffectListenerInputs = (effectBody, context) => {
|
|
11237
11238
|
const registrations = [];
|
|
@@ -11247,7 +11248,7 @@ const collectEffectListenerInputs = (effectBody, context) => {
|
|
|
11247
11248
|
const finalEffectStatement = effectBody.body[effectBody.body.length - 1];
|
|
11248
11249
|
const hasCanonicalCleanupReturn = isNodeOfType(finalEffectStatement, "ReturnStatement");
|
|
11249
11250
|
walkAst(effectBody, (child) => {
|
|
11250
|
-
if (child !== effectBody && isFunctionLike$
|
|
11251
|
+
if (child !== effectBody && isFunctionLike$1(child)) return false;
|
|
11251
11252
|
if (isNodeOfType(child, "ClassDeclaration") || isNodeOfType(child, "ClassExpression")) return false;
|
|
11252
11253
|
if (isNodeOfType(child, "CallExpression")) {
|
|
11253
11254
|
if (isListenerPathAmbiguous(child, effectBody)) return;
|
|
@@ -11295,7 +11296,7 @@ const resolveCalledCleanup = (callNode, context) => {
|
|
|
11295
11296
|
const cleanupSymbol = resolveAliasedSymbol(callee, context, /* @__PURE__ */ new Set());
|
|
11296
11297
|
if (!cleanupSymbol?.initializer || cleanupSymbol.kind !== "const" && cleanupSymbol.kind !== "function") return null;
|
|
11297
11298
|
const initializer = stripParenExpression(cleanupSymbol.initializer);
|
|
11298
|
-
if (!isFunctionLike$
|
|
11299
|
+
if (!isFunctionLike$1(initializer) || initializer.async || initializer.generator) return null;
|
|
11299
11300
|
return {
|
|
11300
11301
|
body: initializer.body,
|
|
11301
11302
|
symbolId: cleanupSymbol.id
|
|
@@ -11342,7 +11343,7 @@ const analyzeCleanupBody = (cleanupBody, context, visitedCleanupSymbolIds = /* @
|
|
|
11342
11343
|
return false;
|
|
11343
11344
|
};
|
|
11344
11345
|
walkAst(cleanupBody, (child) => {
|
|
11345
|
-
if (child !== cleanupBody && isFunctionLike$
|
|
11346
|
+
if (child !== cleanupBody && isFunctionLike$1(child)) return false;
|
|
11346
11347
|
if (isNodeOfType(child, "ClassDeclaration") || isNodeOfType(child, "ClassExpression")) return false;
|
|
11347
11348
|
if (isNodeOfType(child, "ReturnStatement") && child === finalCleanupStatement) return;
|
|
11348
11349
|
if (isNodeOfType(child, "ReturnStatement") || isNodeOfType(child, "ThrowStatement") || isLoopBody && (isNodeOfType(child, "BreakStatement") || isNodeOfType(child, "ContinueStatement"))) {
|
|
@@ -11452,7 +11453,7 @@ const effectListenerCleanupMismatch = defineRule({
|
|
|
11452
11453
|
create: (context) => ({ CallExpression(node) {
|
|
11453
11454
|
if (!isReactApiCall(node, LISTENER_EFFECT_HOOK_NAMES, context.scopes)) return;
|
|
11454
11455
|
const effectCallback = getEffectCallback(node);
|
|
11455
|
-
if (!isFunctionLike$
|
|
11456
|
+
if (!isFunctionLike$1(effectCallback)) return;
|
|
11456
11457
|
const listenerAnalysis = analyzeEffectListeners(effectCallback.body, context);
|
|
11457
11458
|
if (!listenerAnalysis) return;
|
|
11458
11459
|
for (const registration of listenerAnalysis.registrations) {
|
|
@@ -11514,7 +11515,7 @@ const collectEffectInvokedFunctions = (effectCallback) => {
|
|
|
11514
11515
|
const pendingFunctions = [effectCallback];
|
|
11515
11516
|
const enqueue = (candidate) => {
|
|
11516
11517
|
const strippedCandidate = candidate ? stripParenExpression(candidate) : candidate;
|
|
11517
|
-
if (!isFunctionLike$
|
|
11518
|
+
if (!isFunctionLike$1(strippedCandidate) || invokedFunctions.has(strippedCandidate)) return;
|
|
11518
11519
|
invokedFunctions.add(strippedCandidate);
|
|
11519
11520
|
pendingFunctions.push(strippedCandidate);
|
|
11520
11521
|
};
|
|
@@ -11522,18 +11523,18 @@ const collectEffectInvokedFunctions = (effectCallback) => {
|
|
|
11522
11523
|
const currentFunction = pendingFunctions.pop();
|
|
11523
11524
|
if (!currentFunction) break;
|
|
11524
11525
|
walkAst(currentFunction, (child) => {
|
|
11525
|
-
if (child !== currentFunction && isFunctionLike$
|
|
11526
|
+
if (child !== currentFunction && isFunctionLike$1(child)) {
|
|
11526
11527
|
if (isNodeOfType(child, "FunctionDeclaration") && isNodeOfType(child.id, "Identifier")) localFunctionBindings.set(child.id.name, child);
|
|
11527
11528
|
return false;
|
|
11528
11529
|
}
|
|
11529
11530
|
if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier")) {
|
|
11530
11531
|
const initializer = child.init ? stripParenExpression(child.init) : null;
|
|
11531
|
-
if (isFunctionLike$
|
|
11532
|
+
if (isFunctionLike$1(initializer)) localFunctionBindings.set(child.id.name, initializer);
|
|
11532
11533
|
return;
|
|
11533
11534
|
}
|
|
11534
11535
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
11535
11536
|
const callee = stripParenExpression(child.callee);
|
|
11536
|
-
if (isFunctionLike$
|
|
11537
|
+
if (isFunctionLike$1(callee)) {
|
|
11537
11538
|
enqueue(callee);
|
|
11538
11539
|
return;
|
|
11539
11540
|
}
|
|
@@ -11695,7 +11696,7 @@ const hasReactRefCurrentOrigin = (node, scopes, visitedSymbolIds = /* @__PURE__
|
|
|
11695
11696
|
//#region src/plugin/utils/walk-inside-statement-blocks.ts
|
|
11696
11697
|
const walkInsideStatementBlocks = (node, visitor) => {
|
|
11697
11698
|
if (!node || typeof node !== "object") return;
|
|
11698
|
-
if (isFunctionLike$
|
|
11699
|
+
if (isFunctionLike$1(node)) return;
|
|
11699
11700
|
visitor(node);
|
|
11700
11701
|
const nodeRecord = node;
|
|
11701
11702
|
for (const key of Object.keys(nodeRecord)) {
|
|
@@ -11810,7 +11811,7 @@ const resolveExpressionKey = (expression, context, visitedSymbolIds = /* @__PURE
|
|
|
11810
11811
|
}
|
|
11811
11812
|
if (isNodeOfType(unwrappedExpression, "ThisExpression")) return "this";
|
|
11812
11813
|
if (isNodeOfType(unwrappedExpression, "Literal") && (typeof unwrappedExpression.value === "string" || typeof unwrappedExpression.value === "number")) return `literal:${String(unwrappedExpression.value)}`;
|
|
11813
|
-
if (isFunctionLike$
|
|
11814
|
+
if (isFunctionLike$1(unwrappedExpression)) {
|
|
11814
11815
|
const rangeStart = getRangeStart(unwrappedExpression);
|
|
11815
11816
|
return rangeStart === null ? null : `function:${rangeStart}`;
|
|
11816
11817
|
}
|
|
@@ -11853,7 +11854,7 @@ const findSubscribeLikeUsages = (callback, context) => {
|
|
|
11853
11854
|
}
|
|
11854
11855
|
const effectInvokedFunctions = collectEffectInvokedFunctions(callback);
|
|
11855
11856
|
walkAst(callback, (child) => {
|
|
11856
|
-
if (child !== callback && isFunctionLike$
|
|
11857
|
+
if (child !== callback && isFunctionLike$1(child)) {
|
|
11857
11858
|
if (child === cleanupArgument) return false;
|
|
11858
11859
|
if (!effectInvokedFunctions.has(child) && !isSynchronousIteratorCallback(child)) return false;
|
|
11859
11860
|
}
|
|
@@ -12016,7 +12017,7 @@ const isAssignmentFormForOfIteratorReference = (expression, context) => {
|
|
|
12016
12017
|
return didAssignReferencedSymbol;
|
|
12017
12018
|
};
|
|
12018
12019
|
let currentNode = unwrappedExpression.parent;
|
|
12019
|
-
while (currentNode && !isFunctionLike$
|
|
12020
|
+
while (currentNode && !isFunctionLike$1(currentNode)) {
|
|
12020
12021
|
if (isNodeOfType(currentNode, "ForOfStatement")) {
|
|
12021
12022
|
const loopTarget = stripParenExpression(currentNode.left);
|
|
12022
12023
|
if (!isNodeOfType(loopTarget, "VariableDeclaration") && (assignsReferencedSymbol(loopTarget, false) || assignsReferencedSymbol(currentNode.body, true))) return true;
|
|
@@ -12085,8 +12086,8 @@ const resolveIteratorCollectionKey = (expression, context) => {
|
|
|
12085
12086
|
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
12086
12087
|
if (!symbol || symbol.kind !== "parameter") return null;
|
|
12087
12088
|
let callbackNode = symbol.bindingIdentifier.parent;
|
|
12088
|
-
while (callbackNode && !isFunctionLike$
|
|
12089
|
-
if (!callbackNode || !isFunctionLike$
|
|
12089
|
+
while (callbackNode && !isFunctionLike$1(callbackNode)) callbackNode = callbackNode.parent;
|
|
12090
|
+
if (!callbackNode || !isFunctionLike$1(callbackNode)) return null;
|
|
12090
12091
|
const callNode = callbackNode.parent;
|
|
12091
12092
|
if (!isNodeOfType(callNode, "CallExpression")) return null;
|
|
12092
12093
|
const callee = stripParenExpression(callNode.callee);
|
|
@@ -12116,7 +12117,7 @@ const isDirectExhaustiveForOfRelease = (releaseNode, forOfStatement) => {
|
|
|
12116
12117
|
let hasAbruptLoopExit = false;
|
|
12117
12118
|
walkAst(forOfStatement.body, (child) => {
|
|
12118
12119
|
if (hasAbruptLoopExit) return false;
|
|
12119
|
-
if (child !== forOfStatement.body && isFunctionLike$
|
|
12120
|
+
if (child !== forOfStatement.body && isFunctionLike$1(child)) return false;
|
|
12120
12121
|
if (isNodeOfType(child, "BreakStatement") || isNodeOfType(child, "ContinueStatement") || isNodeOfType(child, "ReturnStatement") || isNodeOfType(child, "ThrowStatement")) {
|
|
12121
12122
|
hasAbruptLoopExit = true;
|
|
12122
12123
|
return false;
|
|
@@ -12145,7 +12146,7 @@ const findMappedResourceCollectionKey = (resourceNode, context) => {
|
|
|
12145
12146
|
if (!isNodeOfType(resourceDeclarator, "VariableDeclarator") || resourceDeclarator.init !== resourceRoot || !isNodeOfType(resourceDeclarator.id, "Identifier") || !isNodeOfType(resourceDeclaration, "VariableDeclaration") || resourceDeclaration.kind !== "const" || resourceDeclaration.parent !== callbackNode.body) return null;
|
|
12146
12147
|
const returnStatements = [];
|
|
12147
12148
|
walkAst(callbackNode.body, (child) => {
|
|
12148
|
-
if (child !== callbackNode.body && isFunctionLike$
|
|
12149
|
+
if (child !== callbackNode.body && isFunctionLike$1(child)) return false;
|
|
12149
12150
|
if (isNodeOfType(child, "ReturnStatement")) returnStatements.push(child);
|
|
12150
12151
|
});
|
|
12151
12152
|
const returnStatement = returnStatements[0];
|
|
@@ -12164,7 +12165,7 @@ const findContainingCollectionKey = (resourceNode, context) => {
|
|
|
12164
12165
|
let currentNode = resourceNode;
|
|
12165
12166
|
let parentNode = currentNode.parent;
|
|
12166
12167
|
while (parentNode) {
|
|
12167
|
-
if (isFunctionLike$
|
|
12168
|
+
if (isFunctionLike$1(parentNode)) return null;
|
|
12168
12169
|
if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode) return resolveExpressionKey(parentNode.id, context);
|
|
12169
12170
|
currentNode = parentNode;
|
|
12170
12171
|
parentNode = currentNode.parent;
|
|
@@ -12189,7 +12190,7 @@ const resolveStableValue = (expression, context, visitedSymbolIds = /* @__PURE__
|
|
|
12189
12190
|
if (!isNodeOfType(unwrappedExpression, "Identifier")) return unwrappedExpression;
|
|
12190
12191
|
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
12191
12192
|
const isUnreassignedMutableBinding = (symbol?.kind === "let" || symbol?.kind === "var") && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && symbol.declarationNode.id === symbol.bindingIdentifier && symbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier)) && symbol.scope.symbols.filter((candidate) => candidate.name === symbol.name).length === 1;
|
|
12192
|
-
const recursiveFunctionSymbol = symbol?.kind === "function" && isFunctionLike$
|
|
12193
|
+
const recursiveFunctionSymbol = symbol?.kind === "function" && isFunctionLike$1(symbol.declarationNode) ? context.scopes.ownScopeFor(symbol.declarationNode)?.symbols.find((candidate) => candidate.name === symbol.name && candidate.declarationNode === symbol.declarationNode) : null;
|
|
12193
12194
|
const isUnreassignedFunctionBinding = symbol?.kind === "function" && symbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier)) && (!recursiveFunctionSymbol || recursiveFunctionSymbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier))) && symbol.scope.symbols.filter((candidate) => candidate.name === symbol.name).length === 1;
|
|
12194
12195
|
if (!symbol || symbol.kind !== "const" && !isUnreassignedMutableBinding && !isUnreassignedFunctionBinding || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return unwrappedExpression;
|
|
12195
12196
|
visitedSymbolIds.add(symbol.id);
|
|
@@ -12265,16 +12266,16 @@ const resolveSingleAssignedCleanupFunction = (expression, usage, context) => {
|
|
|
12265
12266
|
const assignmentNode = assignmentTarget.parent;
|
|
12266
12267
|
if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction$1(assignmentNode) !== findEnclosingFunction$1(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [assignmentNode], context)) return null;
|
|
12267
12268
|
const assignedValue = stripParenExpression(assignmentNode.right);
|
|
12268
|
-
return isFunctionLike$
|
|
12269
|
+
return isFunctionLike$1(assignedValue) ? assignedValue : null;
|
|
12269
12270
|
};
|
|
12270
12271
|
const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visitedFunctions = /* @__PURE__ */ new Set()) => {
|
|
12271
|
-
if (!isFunctionLike$
|
|
12272
|
+
if (!isFunctionLike$1(cleanupFunction) || visitedFunctions.has(cleanupFunction)) return false;
|
|
12272
12273
|
visitedFunctions.add(cleanupFunction);
|
|
12273
12274
|
let didCleanupFunctionMatch = false;
|
|
12274
12275
|
const matchingLoopOrHelperAnchors = [];
|
|
12275
12276
|
walkAst(cleanupFunction.body, (cleanupChild) => {
|
|
12276
12277
|
if (didCleanupFunctionMatch) return false;
|
|
12277
|
-
if (cleanupChild !== cleanupFunction.body && isFunctionLike$
|
|
12278
|
+
if (cleanupChild !== cleanupFunction.body && isFunctionLike$1(cleanupChild) && !isSynchronousIteratorCallback(cleanupChild)) return false;
|
|
12278
12279
|
const cleanupCall = isNodeOfType(cleanupChild, "ChainExpression") ? cleanupChild.expression : cleanupChild;
|
|
12279
12280
|
if (doesReleaseCallMatchUsage(cleanupChild, usage, context)) {
|
|
12280
12281
|
const cleanupForOfStatement = findForOfStatementForIteratorExpression(isNodeOfType(cleanupCall, "CallExpression") ? cleanupCall.arguments?.[0] : null, context);
|
|
@@ -12288,7 +12289,7 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
|
|
|
12288
12289
|
if (!isNodeOfType(cleanupCall, "CallExpression")) return;
|
|
12289
12290
|
const stableHelperFunction = resolveStableValue(cleanupCall.callee, context);
|
|
12290
12291
|
const helperFunction = isNodeOfType(stableHelperFunction, "Identifier") ? resolveSingleAssignedCleanupFunction(stableHelperFunction, usage, context) : stableHelperFunction;
|
|
12291
|
-
if (helperFunction && isFunctionLike$
|
|
12292
|
+
if (helperFunction && isFunctionLike$1(helperFunction) && !helperFunction.async && !helperFunction.generator && doesCleanupFunctionReleaseUsage(helperFunction, usage, context, new Set(visitedFunctions))) matchingLoopOrHelperAnchors.push(cleanupCall);
|
|
12292
12293
|
});
|
|
12293
12294
|
return didCleanupFunctionMatch || doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, matchingLoopOrHelperAnchors, context);
|
|
12294
12295
|
};
|
|
@@ -12312,7 +12313,7 @@ const callbackReturnsCleanupForUsage = (callback, usage, context) => {
|
|
|
12312
12313
|
if (doesBoundCleanupReleaseUsage(returnedValue, usage, context)) return true;
|
|
12313
12314
|
const cleanupFunction = resolveStableValue(returnedValue, context);
|
|
12314
12315
|
if (cleanupFunction && doesBoundCleanupReleaseUsage(cleanupFunction, usage, context)) return true;
|
|
12315
|
-
return Boolean(cleanupFunction && isFunctionLike$
|
|
12316
|
+
return Boolean(cleanupFunction && isFunctionLike$1(cleanupFunction) && doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context));
|
|
12316
12317
|
};
|
|
12317
12318
|
if (!isNodeOfType(callback.body, "BlockStatement")) return doesReturnedValueReleaseUsage(stripParenExpression(callback.body));
|
|
12318
12319
|
const matchingCleanupReturns = [];
|
|
@@ -12347,7 +12348,7 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
|
|
|
12347
12348
|
return;
|
|
12348
12349
|
}
|
|
12349
12350
|
const helperFunction = resolveStableValue(child.callee, context);
|
|
12350
|
-
if (helperFunction && isFunctionLike$
|
|
12351
|
+
if (helperFunction && isFunctionLike$1(helperFunction) && doesCleanupFunctionReleaseUsage(helperFunction, usage, context)) matchingReleaseAnchors.push(handleGuard ?? child);
|
|
12351
12352
|
});
|
|
12352
12353
|
return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
|
|
12353
12354
|
};
|
|
@@ -12405,10 +12406,10 @@ const isDirectEarlyReturnConsequent = (ifStatement) => {
|
|
|
12405
12406
|
return isNodeOfType(ifStatement.consequent, "BlockStatement") && ifStatement.consequent.body.length === 1 && isNodeOfType(ifStatement.consequent.body[0], "ReturnStatement");
|
|
12406
12407
|
};
|
|
12407
12408
|
const collectDeferredUsageGuardStates = (callback, usageNode, context) => {
|
|
12408
|
-
if (!isFunctionLike$
|
|
12409
|
+
if (!isFunctionLike$1(callback) || callback.async) return [];
|
|
12409
12410
|
const guardStates = [];
|
|
12410
12411
|
walkAst(callback.body, (child) => {
|
|
12411
|
-
if (child !== callback.body && isFunctionLike$
|
|
12412
|
+
if (child !== callback.body && isFunctionLike$1(child)) return false;
|
|
12412
12413
|
if (isNodeOfType(child, "IfStatement") && isDirectEarlyReturnConsequent(child) && doMatchingNodesCoverEveryPathBeforeUsage(usageNode, [child], callback, context)) guardStates.push(...collectBlockingBooleanStates(child.test, true, context));
|
|
12413
12414
|
});
|
|
12414
12415
|
let descendant = usageNode;
|
|
@@ -12423,11 +12424,11 @@ const collectDeferredUsageGuardStates = (callback, usageNode, context) => {
|
|
|
12423
12424
|
const cleanupReturnInvalidatesGuard = (cleanupReturn, guardState, context) => {
|
|
12424
12425
|
if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return false;
|
|
12425
12426
|
const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
|
|
12426
|
-
if (!cleanupFunction || !isFunctionLike$
|
|
12427
|
+
if (!cleanupFunction || !isFunctionLike$1(cleanupFunction) || cleanupFunction.async) return false;
|
|
12427
12428
|
let didInvalidateGuard = false;
|
|
12428
12429
|
walkAst(cleanupFunction.body, (child) => {
|
|
12429
12430
|
if (didInvalidateGuard) return false;
|
|
12430
|
-
if (child !== cleanupFunction.body && isFunctionLike$
|
|
12431
|
+
if (child !== cleanupFunction.body && isFunctionLike$1(child)) return false;
|
|
12431
12432
|
if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveExpressionKey(child.left, context) === guardState.key && readStaticBoolean(child.right) === guardState.value && context.cfg.isUnconditionalFromEntry(child)) {
|
|
12432
12433
|
didInvalidateGuard = true;
|
|
12433
12434
|
return false;
|
|
@@ -12437,11 +12438,11 @@ const cleanupReturnInvalidatesGuard = (cleanupReturn, guardState, context) => {
|
|
|
12437
12438
|
};
|
|
12438
12439
|
const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, context) => {
|
|
12439
12440
|
const usageStart = getRangeStart(usageNode);
|
|
12440
|
-
if (!isFunctionLike$
|
|
12441
|
+
if (!isFunctionLike$1(callback) || usageStart === null) return true;
|
|
12441
12442
|
let didWriteGuard = false;
|
|
12442
12443
|
walkAst(callback.body, (child) => {
|
|
12443
12444
|
if (didWriteGuard) return false;
|
|
12444
|
-
if (child !== callback.body && isFunctionLike$
|
|
12445
|
+
if (child !== callback.body && isFunctionLike$1(child)) return false;
|
|
12445
12446
|
const childStart = getRangeStart(child);
|
|
12446
12447
|
if (childStart === null || childStart >= usageStart) return;
|
|
12447
12448
|
const writtenExpression = isNodeOfType(child, "AssignmentExpression") ? child.left : isNodeOfType(child, "UpdateExpression") ? child.argument : null;
|
|
@@ -12494,7 +12495,7 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
|
12494
12495
|
matchingCleanupReturns.push(child);
|
|
12495
12496
|
return;
|
|
12496
12497
|
}
|
|
12497
|
-
if (!cleanupFunction || !isFunctionLike$
|
|
12498
|
+
if (!cleanupFunction || !isFunctionLike$1(cleanupFunction)) return;
|
|
12498
12499
|
if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
|
|
12499
12500
|
});
|
|
12500
12501
|
if (hasGuardedDeferredCleanup(callback, usage, matchingCleanupReturns, context)) return true;
|
|
@@ -12664,19 +12665,19 @@ const fileContainsReleaseForUsage = (usage, context) => {
|
|
|
12664
12665
|
};
|
|
12665
12666
|
const resolveRefOwnedCleanupFunction = (expression, context) => {
|
|
12666
12667
|
const resolvedExpression = resolveStableValue(expression, context);
|
|
12667
|
-
if (isFunctionLike$
|
|
12668
|
+
if (isFunctionLike$1(resolvedExpression)) return resolvedExpression;
|
|
12668
12669
|
if (!isNodeOfType(resolvedExpression, "CallExpression") || !isReactApiCall(resolvedExpression, "useCallback", context.scopes)) return null;
|
|
12669
12670
|
return getEffectCallback(resolvedExpression);
|
|
12670
12671
|
};
|
|
12671
12672
|
const findRefOwnedHandlerStorage = (retainedFunction, usage, context) => {
|
|
12672
|
-
if (!isFunctionLike$
|
|
12673
|
+
if (!isFunctionLike$1(retainedFunction) || usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.handlerKey === null || !usage.receiverKey?.startsWith("global:") || !usage.eventKey?.startsWith("literal:")) return null;
|
|
12673
12674
|
const usageStart = getRangeStart(usage.node);
|
|
12674
12675
|
const functionCfg = context.cfg.cfgFor(retainedFunction);
|
|
12675
12676
|
const usageBlock = functionCfg?.blockOf(usage.node);
|
|
12676
12677
|
if (usageStart === null || !functionCfg || !usageBlock) return null;
|
|
12677
12678
|
const matchingStorage = [];
|
|
12678
12679
|
walkAst(retainedFunction.body, (child) => {
|
|
12679
|
-
if (child !== retainedFunction.body && isFunctionLike$
|
|
12680
|
+
if (child !== retainedFunction.body && isFunctionLike$1(child)) return false;
|
|
12680
12681
|
if (!isNodeOfType(child, "AssignmentExpression") || child.operator !== "=") return;
|
|
12681
12682
|
const assignmentStart = getRangeStart(child);
|
|
12682
12683
|
const refCurrentExpression = stripParenExpression(child.left);
|
|
@@ -12733,17 +12734,17 @@ const doMatchingNodesCoverEveryPathBeforeUsage = (usageNode, matchingNodes, owne
|
|
|
12733
12734
|
return true;
|
|
12734
12735
|
};
|
|
12735
12736
|
const retainedFunctionReleasesPreviousRefOwnedUsage = (retainedFunction, cleanupFunction, storageNode, context) => {
|
|
12736
|
-
if (!isFunctionLike$
|
|
12737
|
+
if (!isFunctionLike$1(retainedFunction)) return false;
|
|
12737
12738
|
const retainedFunctionBody = retainedFunction.body;
|
|
12738
12739
|
const cleanupCalls = [];
|
|
12739
12740
|
walkAst(retainedFunctionBody, (child) => {
|
|
12740
|
-
if (child !== retainedFunctionBody && isFunctionLike$
|
|
12741
|
+
if (child !== retainedFunctionBody && isFunctionLike$1(child)) return false;
|
|
12741
12742
|
if (isNodeOfType(child, "CallExpression") && resolveRefOwnedCleanupFunction(child.callee, context) === cleanupFunction) cleanupCalls.push(child);
|
|
12742
12743
|
});
|
|
12743
12744
|
return doMatchingNodesCoverEveryPathBeforeUsage(storageNode, cleanupCalls, retainedFunction, context);
|
|
12744
12745
|
};
|
|
12745
12746
|
const isDirectRefOwnedRelease = (releaseNode, cleanupFunction, usage, storedHandlerKey, refCurrentKey, context) => {
|
|
12746
|
-
if (!isFunctionLike$
|
|
12747
|
+
if (!isFunctionLike$1(cleanupFunction)) return false;
|
|
12747
12748
|
const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
|
|
12748
12749
|
if (!isNodeOfType(releaseCall, "CallExpression") || !isNodeOfType(releaseCall.callee, "MemberExpression") || releaseCall.callee.computed || !isNodeOfType(releaseCall.callee.property, "Identifier") || releaseCall.callee.property.name !== "removeEventListener" || !isNodeOfType(usage.node, "CallExpression") || usage.node.arguments?.[2] !== void 0 || releaseCall.arguments?.[2] !== void 0) return false;
|
|
12749
12750
|
const releaseStatement = findTransparentExpressionRoot(releaseNode).parent;
|
|
@@ -12763,13 +12764,13 @@ const isRefPresenceGuardedEarlyReturn = (returnStatement, refCurrentKey, context
|
|
|
12763
12764
|
return isNodeOfType(guardTest, "UnaryExpression") && guardTest.operator === "!" && resolveExpressionKey(guardTest.argument, context) === refCurrentKey;
|
|
12764
12765
|
};
|
|
12765
12766
|
const hasUnprovenReturnBeforeRefOwnedRelease = (cleanupFunction, releaseNode, refCurrentKey, context) => {
|
|
12766
|
-
if (!isFunctionLike$
|
|
12767
|
+
if (!isFunctionLike$1(cleanupFunction)) return true;
|
|
12767
12768
|
const releaseStart = getRangeStart(releaseNode);
|
|
12768
12769
|
if (releaseStart === null) return true;
|
|
12769
12770
|
let hasUnprovenEarlyReturn = false;
|
|
12770
12771
|
walkAst(cleanupFunction.body, (child) => {
|
|
12771
12772
|
if (hasUnprovenEarlyReturn) return false;
|
|
12772
|
-
if (child !== cleanupFunction.body && isFunctionLike$
|
|
12773
|
+
if (child !== cleanupFunction.body && isFunctionLike$1(child)) return false;
|
|
12773
12774
|
if (!isNodeOfType(child, "ReturnStatement")) return;
|
|
12774
12775
|
const returnStart = getRangeStart(child);
|
|
12775
12776
|
if ((returnStart === null || returnStart < releaseStart) && !isRefPresenceGuardedEarlyReturn(child, refCurrentKey, context)) {
|
|
@@ -12780,14 +12781,14 @@ const hasUnprovenReturnBeforeRefOwnedRelease = (cleanupFunction, releaseNode, re
|
|
|
12780
12781
|
return hasUnprovenEarlyReturn;
|
|
12781
12782
|
};
|
|
12782
12783
|
const cleanupFunctionReleasesRefOwnedUsage = (cleanupFunction, componentFunction, retainedFunction, usage, context) => {
|
|
12783
|
-
if (!isFunctionLike$
|
|
12784
|
+
if (!isFunctionLike$1(cleanupFunction) || !isFunctionLike$1(componentFunction) || cleanupFunction.async || cleanupFunction.generator) return false;
|
|
12784
12785
|
const storage = findRefOwnedHandlerStorage(retainedFunction, usage, context);
|
|
12785
12786
|
if (!storage) return false;
|
|
12786
12787
|
if (!retainedFunctionReleasesPreviousRefOwnedUsage(retainedFunction, cleanupFunction, storage.assignmentNode, context)) return false;
|
|
12787
12788
|
let releaseNode = null;
|
|
12788
12789
|
walkAst(cleanupFunction.body, (child) => {
|
|
12789
12790
|
if (releaseNode) return false;
|
|
12790
|
-
if (child !== cleanupFunction.body && isFunctionLike$
|
|
12791
|
+
if (child !== cleanupFunction.body && isFunctionLike$1(child)) return false;
|
|
12791
12792
|
if (isDirectRefOwnedRelease(child, cleanupFunction, usage, storage.handlerKey, storage.refCurrentKey, context)) {
|
|
12792
12793
|
releaseNode = child;
|
|
12793
12794
|
return false;
|
|
@@ -12844,7 +12845,7 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
|
|
|
12844
12845
|
const cleanupFunction = resolveRefOwnedCleanupFunction(returnedValue, context);
|
|
12845
12846
|
return Boolean(cleanupFunction && cleanupFunctionReleasesRefOwnedUsage(cleanupFunction, componentFunction, retainedFunction, usage, context));
|
|
12846
12847
|
};
|
|
12847
|
-
if (!isFunctionLike$
|
|
12848
|
+
if (!isFunctionLike$1(effectCallback)) return false;
|
|
12848
12849
|
if (!isNodeOfType(effectCallback.body, "BlockStatement")) return matchesReturnedCleanup(stripParenExpression(effectCallback.body));
|
|
12849
12850
|
const matchingReturns = [];
|
|
12850
12851
|
walkInsideStatementBlocks(effectCallback.body, (child) => {
|
|
@@ -12854,7 +12855,7 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
|
|
|
12854
12855
|
};
|
|
12855
12856
|
const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
|
|
12856
12857
|
const componentFunction = findEnclosingFunction$1(retainedFunction);
|
|
12857
|
-
if (!componentFunction || !isFunctionLike$
|
|
12858
|
+
if (!componentFunction || !isFunctionLike$1(componentFunction)) return false;
|
|
12858
12859
|
let didFindCleanupEffect = false;
|
|
12859
12860
|
walkAst(componentFunction.body, (child) => {
|
|
12860
12861
|
if (didFindCleanupEffect) return false;
|
|
@@ -12903,7 +12904,7 @@ const doesResourceResultEscape = (resourceNode, allowConciseReturnEscape) => {
|
|
|
12903
12904
|
return false;
|
|
12904
12905
|
};
|
|
12905
12906
|
const findRetainedFunctionLeak = (retainedFunction, context) => {
|
|
12906
|
-
if (!isFunctionLike$
|
|
12907
|
+
if (!isFunctionLike$1(retainedFunction)) return null;
|
|
12907
12908
|
const body = retainedFunction.body;
|
|
12908
12909
|
if (!body) return null;
|
|
12909
12910
|
let leak = null;
|
|
@@ -12912,7 +12913,7 @@ const findRetainedFunctionLeak = (retainedFunction, context) => {
|
|
|
12912
12913
|
const hasReleaseForUsage = (usage) => isExternalStoreSubscribeFunction ? effectHasCleanupForUsage(retainedFunction, usage, context) : fileContainsReleaseForUsage(usage, context) || hasGuaranteedRefOwnedUnmountCleanup(retainedFunction, usage, context);
|
|
12913
12914
|
walkAst(body, (child) => {
|
|
12914
12915
|
if (leak !== null) return false;
|
|
12915
|
-
if (isFunctionLike$
|
|
12916
|
+
if (isFunctionLike$1(child)) return false;
|
|
12916
12917
|
if (isSocketConstruction(child) && !doesResourceResultEscape(child, false)) {
|
|
12917
12918
|
const socketUsage = {
|
|
12918
12919
|
kind: "socket",
|
|
@@ -12973,7 +12974,7 @@ const isDirectJsxEventHandlerValue = (expression) => {
|
|
|
12973
12974
|
return isNodeOfType(expressionContainer, "JSXExpressionContainer") && expressionContainer.expression === expressionRoot && isEventHandlerAttribute(expressionContainer.parent);
|
|
12974
12975
|
};
|
|
12975
12976
|
const isInlineRetainedHandlerFunction = (functionNode, context) => {
|
|
12976
|
-
if (!isFunctionLike$
|
|
12977
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
12977
12978
|
const functionRoot = findTransparentExpressionRoot(functionNode);
|
|
12978
12979
|
const callbackCall = functionRoot.parent;
|
|
12979
12980
|
if (isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments?.[0] === functionRoot && isHookCall$2(callbackCall, "useCallback") && isDirectJsxEventHandlerValue(callbackCall)) return true;
|
|
@@ -13166,7 +13167,7 @@ const recordReference = (state, identifier, flag) => {
|
|
|
13166
13167
|
};
|
|
13167
13168
|
const isFunctionBodyBlock = (block) => {
|
|
13168
13169
|
if (!block.parent) return false;
|
|
13169
|
-
return isFunctionLike$
|
|
13170
|
+
return isFunctionLike$1(block.parent);
|
|
13170
13171
|
};
|
|
13171
13172
|
const isCatchClauseBlock = (block) => block.parent !== null && block.parent !== void 0 && block.parent.type === "CatchClause";
|
|
13172
13173
|
const handleVariableDeclaration = (declaration, state) => {
|
|
@@ -13321,7 +13322,7 @@ const walkParameterReferences = (pattern, state) => {
|
|
|
13321
13322
|
if (isNodeOfType(pattern, "RestElement")) walkParameterReferences(pattern.argument, state);
|
|
13322
13323
|
};
|
|
13323
13324
|
const walk = (node, state) => {
|
|
13324
|
-
if (isFunctionLike$
|
|
13325
|
+
if (isFunctionLike$1(node)) {
|
|
13325
13326
|
if (isNodeOfType(node, "FunctionDeclaration") && node.id) handleFunctionDeclaration(node, state);
|
|
13326
13327
|
const functionParams = node.params ?? [];
|
|
13327
13328
|
for (const param of functionParams) {
|
|
@@ -13555,7 +13556,7 @@ const computeClosureCaptures = (functionNode, scopes) => {
|
|
|
13555
13556
|
const out = [];
|
|
13556
13557
|
const seen = /* @__PURE__ */ new Set();
|
|
13557
13558
|
walkAst(functionNode, (node) => {
|
|
13558
|
-
if (node !== functionNode && isFunctionLike$
|
|
13559
|
+
if (node !== functionNode && isFunctionLike$1(node)) {
|
|
13559
13560
|
const innerCaptures = closureCaptures(node, scopes);
|
|
13560
13561
|
for (const reference of innerCaptures) if (reference.resolvedSymbol && !isDescendantScope(reference.resolvedSymbol.scope, functionScope)) {
|
|
13561
13562
|
if (!seen.has(reference.id)) {
|
|
@@ -13591,6 +13592,768 @@ const closureCaptures = (functionNode, scopes) => {
|
|
|
13591
13592
|
return computedCaptures;
|
|
13592
13593
|
};
|
|
13593
13594
|
//#endregion
|
|
13595
|
+
//#region src/plugin/semantic/control-flow-graph.ts
|
|
13596
|
+
let nextBlockId = 0;
|
|
13597
|
+
const createBlock = (builder) => {
|
|
13598
|
+
const block = {
|
|
13599
|
+
id: nextBlockId++,
|
|
13600
|
+
nodes: [],
|
|
13601
|
+
successors: [],
|
|
13602
|
+
predecessors: []
|
|
13603
|
+
};
|
|
13604
|
+
builder.blocks.push(block);
|
|
13605
|
+
return block;
|
|
13606
|
+
};
|
|
13607
|
+
const addEdge = (from, to, kind) => {
|
|
13608
|
+
const edge = {
|
|
13609
|
+
from,
|
|
13610
|
+
to,
|
|
13611
|
+
kind
|
|
13612
|
+
};
|
|
13613
|
+
from.successors.push(edge);
|
|
13614
|
+
to.predecessors.push(edge);
|
|
13615
|
+
};
|
|
13616
|
+
const appendNode = (builder, block, node) => {
|
|
13617
|
+
block.nodes.push(node);
|
|
13618
|
+
if (!builder.nodeBlock.has(node)) builder.nodeBlock.set(node, block);
|
|
13619
|
+
};
|
|
13620
|
+
const mapDescendantsToBlock = (builder, node, block) => {
|
|
13621
|
+
builder.nodeBlock.set(node, block);
|
|
13622
|
+
if (isFunctionLike$1(node)) {
|
|
13623
|
+
builder.nestedFunctions.push(node);
|
|
13624
|
+
return;
|
|
13625
|
+
}
|
|
13626
|
+
const record = node;
|
|
13627
|
+
for (const key of Object.keys(record)) {
|
|
13628
|
+
if (key === "parent") continue;
|
|
13629
|
+
const child = record[key];
|
|
13630
|
+
if (Array.isArray(child)) {
|
|
13631
|
+
for (const item of child) if (isAstNode(item)) mapDescendantsToBlock(builder, item, block);
|
|
13632
|
+
} else if (isAstNode(child)) mapDescendantsToBlock(builder, child, block);
|
|
13633
|
+
}
|
|
13634
|
+
};
|
|
13635
|
+
const hasInternalControlFlow = (node) => {
|
|
13636
|
+
switch (node.type) {
|
|
13637
|
+
case "IfStatement":
|
|
13638
|
+
case "WhileStatement":
|
|
13639
|
+
case "DoWhileStatement":
|
|
13640
|
+
case "ForStatement":
|
|
13641
|
+
case "ForInStatement":
|
|
13642
|
+
case "ForOfStatement":
|
|
13643
|
+
case "SwitchStatement":
|
|
13644
|
+
case "TryStatement":
|
|
13645
|
+
case "ReturnStatement":
|
|
13646
|
+
case "ThrowStatement":
|
|
13647
|
+
case "BreakStatement":
|
|
13648
|
+
case "ContinueStatement":
|
|
13649
|
+
case "BlockStatement":
|
|
13650
|
+
case "LabeledStatement": return true;
|
|
13651
|
+
default: return false;
|
|
13652
|
+
}
|
|
13653
|
+
};
|
|
13654
|
+
const findLabel = (builder, name) => {
|
|
13655
|
+
if (name === null) {
|
|
13656
|
+
if (builder.loopStack.length > 0) {
|
|
13657
|
+
const top = builder.loopStack[builder.loopStack.length - 1];
|
|
13658
|
+
return {
|
|
13659
|
+
merge: top.merge,
|
|
13660
|
+
header: top.header
|
|
13661
|
+
};
|
|
13662
|
+
}
|
|
13663
|
+
if (builder.switchStack.length > 0) return {
|
|
13664
|
+
merge: builder.switchStack[builder.switchStack.length - 1].merge,
|
|
13665
|
+
header: null
|
|
13666
|
+
};
|
|
13667
|
+
return null;
|
|
13668
|
+
}
|
|
13669
|
+
for (let i = builder.labelStack.length - 1; i >= 0; i--) {
|
|
13670
|
+
const entry = builder.labelStack[i];
|
|
13671
|
+
if (entry.label === name) return {
|
|
13672
|
+
merge: entry.merge,
|
|
13673
|
+
header: entry.header
|
|
13674
|
+
};
|
|
13675
|
+
}
|
|
13676
|
+
return null;
|
|
13677
|
+
};
|
|
13678
|
+
const buildStatements = (builder, statements, current) => {
|
|
13679
|
+
let cursor = current;
|
|
13680
|
+
for (const statement of statements) cursor = buildStatement(builder, statement, cursor);
|
|
13681
|
+
return cursor;
|
|
13682
|
+
};
|
|
13683
|
+
const buildStatement = (builder, statement, current) => {
|
|
13684
|
+
builder.nodeBlock.set(statement, current);
|
|
13685
|
+
if (!hasInternalControlFlow(statement)) {
|
|
13686
|
+
appendNode(builder, current, statement);
|
|
13687
|
+
mapDescendantsToBlock(builder, statement, current);
|
|
13688
|
+
return current;
|
|
13689
|
+
}
|
|
13690
|
+
if (isNodeOfType(statement, "BlockStatement")) return buildStatements(builder, statement.body, current);
|
|
13691
|
+
if (isNodeOfType(statement, "LabeledStatement")) {
|
|
13692
|
+
const merge = createBlock(builder);
|
|
13693
|
+
const labelEntry = {
|
|
13694
|
+
label: statement.label.name,
|
|
13695
|
+
merge,
|
|
13696
|
+
header: null
|
|
13697
|
+
};
|
|
13698
|
+
builder.labelStack.push(labelEntry);
|
|
13699
|
+
const body = statement.body;
|
|
13700
|
+
const end = buildStatement(builder, body, current);
|
|
13701
|
+
builder.labelStack.pop();
|
|
13702
|
+
addEdge(end, merge, "uncond");
|
|
13703
|
+
return merge;
|
|
13704
|
+
}
|
|
13705
|
+
if (isNodeOfType(statement, "ReturnStatement")) {
|
|
13706
|
+
if (statement.argument) mapDescendantsToBlock(builder, statement.argument, current);
|
|
13707
|
+
addEdge(current, builder.exit, "uncond");
|
|
13708
|
+
return createBlock(builder);
|
|
13709
|
+
}
|
|
13710
|
+
if (isNodeOfType(statement, "ThrowStatement")) {
|
|
13711
|
+
if (statement.argument) mapDescendantsToBlock(builder, statement.argument, current);
|
|
13712
|
+
const top = builder.tryStack[builder.tryStack.length - 1];
|
|
13713
|
+
if (top?.catch) addEdge(current, top.catch, "uncond");
|
|
13714
|
+
else if (top?.finally) addEdge(current, top.finally, "uncond");
|
|
13715
|
+
else addEdge(current, builder.exit, "throw");
|
|
13716
|
+
return createBlock(builder);
|
|
13717
|
+
}
|
|
13718
|
+
if (isNodeOfType(statement, "BreakStatement")) {
|
|
13719
|
+
const target = findLabel(builder, statement.label ? statement.label.name : null);
|
|
13720
|
+
if (target) addEdge(current, target.merge, "uncond");
|
|
13721
|
+
else addEdge(current, builder.exit, "uncond");
|
|
13722
|
+
return createBlock(builder);
|
|
13723
|
+
}
|
|
13724
|
+
if (isNodeOfType(statement, "ContinueStatement")) {
|
|
13725
|
+
const target = findLabel(builder, statement.label ? statement.label.name : null);
|
|
13726
|
+
if (target?.header) addEdge(current, target.header, "uncond");
|
|
13727
|
+
return createBlock(builder);
|
|
13728
|
+
}
|
|
13729
|
+
if (isNodeOfType(statement, "IfStatement")) {
|
|
13730
|
+
mapDescendantsToBlock(builder, statement.test, current);
|
|
13731
|
+
const thenBlock = createBlock(builder);
|
|
13732
|
+
const merge = createBlock(builder);
|
|
13733
|
+
addEdge(current, thenBlock, "cond");
|
|
13734
|
+
addEdge(buildStatement(builder, statement.consequent, thenBlock), merge, "uncond");
|
|
13735
|
+
if (statement.alternate) {
|
|
13736
|
+
const elseBlock = createBlock(builder);
|
|
13737
|
+
addEdge(current, elseBlock, "cond");
|
|
13738
|
+
addEdge(buildStatement(builder, statement.alternate, elseBlock), merge, "uncond");
|
|
13739
|
+
} else addEdge(current, merge, "cond");
|
|
13740
|
+
return merge;
|
|
13741
|
+
}
|
|
13742
|
+
if (isNodeOfType(statement, "WhileStatement") || isNodeOfType(statement, "DoWhileStatement")) {
|
|
13743
|
+
const isDoWhile = isNodeOfType(statement, "DoWhileStatement");
|
|
13744
|
+
mapDescendantsToBlock(builder, statement.test, current);
|
|
13745
|
+
const header = createBlock(builder);
|
|
13746
|
+
const body = createBlock(builder);
|
|
13747
|
+
const merge = createBlock(builder);
|
|
13748
|
+
if (isDoWhile) addEdge(current, body, "uncond");
|
|
13749
|
+
else {
|
|
13750
|
+
addEdge(current, header, "uncond");
|
|
13751
|
+
addEdge(header, body, "cond");
|
|
13752
|
+
addEdge(header, merge, "cond");
|
|
13753
|
+
}
|
|
13754
|
+
builder.loopStack.push({
|
|
13755
|
+
header,
|
|
13756
|
+
merge,
|
|
13757
|
+
label: null
|
|
13758
|
+
});
|
|
13759
|
+
const bodyEnd = buildStatement(builder, statement.body, body);
|
|
13760
|
+
builder.loopStack.pop();
|
|
13761
|
+
if (isDoWhile) {
|
|
13762
|
+
addEdge(bodyEnd, header, "uncond");
|
|
13763
|
+
addEdge(header, body, "cond");
|
|
13764
|
+
addEdge(header, merge, "cond");
|
|
13765
|
+
} else addEdge(bodyEnd, header, "uncond");
|
|
13766
|
+
return merge;
|
|
13767
|
+
}
|
|
13768
|
+
if (isNodeOfType(statement, "ForStatement")) {
|
|
13769
|
+
if (statement.init) mapDescendantsToBlock(builder, statement.init, current);
|
|
13770
|
+
if (statement.test) mapDescendantsToBlock(builder, statement.test, current);
|
|
13771
|
+
const header = createBlock(builder);
|
|
13772
|
+
const body = createBlock(builder);
|
|
13773
|
+
const merge = createBlock(builder);
|
|
13774
|
+
addEdge(current, header, "uncond");
|
|
13775
|
+
addEdge(header, body, "cond");
|
|
13776
|
+
addEdge(header, merge, "cond");
|
|
13777
|
+
builder.loopStack.push({
|
|
13778
|
+
header,
|
|
13779
|
+
merge,
|
|
13780
|
+
label: null
|
|
13781
|
+
});
|
|
13782
|
+
const bodyEnd = buildStatement(builder, statement.body, body);
|
|
13783
|
+
builder.loopStack.pop();
|
|
13784
|
+
if (statement.update) mapDescendantsToBlock(builder, statement.update, header);
|
|
13785
|
+
addEdge(bodyEnd, header, "uncond");
|
|
13786
|
+
return merge;
|
|
13787
|
+
}
|
|
13788
|
+
if (isNodeOfType(statement, "ForInStatement") || isNodeOfType(statement, "ForOfStatement")) {
|
|
13789
|
+
mapDescendantsToBlock(builder, statement.right, current);
|
|
13790
|
+
mapDescendantsToBlock(builder, statement.left, current);
|
|
13791
|
+
const header = createBlock(builder);
|
|
13792
|
+
const body = createBlock(builder);
|
|
13793
|
+
const merge = createBlock(builder);
|
|
13794
|
+
addEdge(current, header, "uncond");
|
|
13795
|
+
addEdge(header, body, "cond");
|
|
13796
|
+
addEdge(header, merge, "cond");
|
|
13797
|
+
builder.loopStack.push({
|
|
13798
|
+
header,
|
|
13799
|
+
merge,
|
|
13800
|
+
label: null
|
|
13801
|
+
});
|
|
13802
|
+
const bodyEnd = buildStatement(builder, statement.body, body);
|
|
13803
|
+
builder.loopStack.pop();
|
|
13804
|
+
addEdge(bodyEnd, header, "uncond");
|
|
13805
|
+
return merge;
|
|
13806
|
+
}
|
|
13807
|
+
if (isNodeOfType(statement, "SwitchStatement")) {
|
|
13808
|
+
mapDescendantsToBlock(builder, statement.discriminant, current);
|
|
13809
|
+
const merge = createBlock(builder);
|
|
13810
|
+
builder.switchStack.push({
|
|
13811
|
+
merge,
|
|
13812
|
+
label: null
|
|
13813
|
+
});
|
|
13814
|
+
let previousCaseEnd = null;
|
|
13815
|
+
let hasDefault = false;
|
|
13816
|
+
for (const switchCase of statement.cases) {
|
|
13817
|
+
const caseBlock = createBlock(builder);
|
|
13818
|
+
addEdge(current, caseBlock, "cond");
|
|
13819
|
+
if (previousCaseEnd) addEdge(previousCaseEnd, caseBlock, "uncond");
|
|
13820
|
+
previousCaseEnd = buildStatements(builder, switchCase.consequent, caseBlock);
|
|
13821
|
+
if (switchCase.test === null) hasDefault = true;
|
|
13822
|
+
}
|
|
13823
|
+
builder.switchStack.pop();
|
|
13824
|
+
if (previousCaseEnd) addEdge(previousCaseEnd, merge, "uncond");
|
|
13825
|
+
if (!hasDefault) addEdge(current, merge, "cond");
|
|
13826
|
+
return merge;
|
|
13827
|
+
}
|
|
13828
|
+
if (isNodeOfType(statement, "TryStatement")) {
|
|
13829
|
+
const tryBlock = createBlock(builder);
|
|
13830
|
+
const merge = createBlock(builder);
|
|
13831
|
+
let catchBlock = null;
|
|
13832
|
+
let finallyBlock = null;
|
|
13833
|
+
if (statement.handler) catchBlock = createBlock(builder);
|
|
13834
|
+
if (statement.finalizer) finallyBlock = createBlock(builder);
|
|
13835
|
+
addEdge(current, tryBlock, "uncond");
|
|
13836
|
+
builder.tryStack.push({
|
|
13837
|
+
catch: catchBlock,
|
|
13838
|
+
finally: finallyBlock
|
|
13839
|
+
});
|
|
13840
|
+
const tryEnd = buildStatements(builder, statement.block.body, tryBlock);
|
|
13841
|
+
builder.tryStack.pop();
|
|
13842
|
+
if (catchBlock) addEdge(tryBlock, catchBlock, "cond");
|
|
13843
|
+
if (statement.handler && catchBlock) {
|
|
13844
|
+
const handlerBody = statement.handler.body;
|
|
13845
|
+
const catchEnd = buildStatement(builder, handlerBody, catchBlock);
|
|
13846
|
+
if (finallyBlock) addEdge(catchEnd, finallyBlock, "uncond");
|
|
13847
|
+
else addEdge(catchEnd, merge, "uncond");
|
|
13848
|
+
}
|
|
13849
|
+
if (finallyBlock && statement.finalizer) {
|
|
13850
|
+
addEdge(tryEnd, finallyBlock, "uncond");
|
|
13851
|
+
addEdge(buildStatements(builder, statement.finalizer.body, finallyBlock), merge, "uncond");
|
|
13852
|
+
} else addEdge(tryEnd, merge, "uncond");
|
|
13853
|
+
return merge;
|
|
13854
|
+
}
|
|
13855
|
+
appendNode(builder, current, statement);
|
|
13856
|
+
mapDescendantsToBlock(builder, statement, current);
|
|
13857
|
+
return current;
|
|
13858
|
+
};
|
|
13859
|
+
const buildFunctionCfg = (functionNode, body, nestedFunctionSink) => {
|
|
13860
|
+
const builder = {
|
|
13861
|
+
blocks: [],
|
|
13862
|
+
entry: null,
|
|
13863
|
+
exit: null,
|
|
13864
|
+
nestedFunctions: nestedFunctionSink,
|
|
13865
|
+
nodeBlock: /* @__PURE__ */ new Map(),
|
|
13866
|
+
loopStack: [],
|
|
13867
|
+
switchStack: [],
|
|
13868
|
+
tryStack: [],
|
|
13869
|
+
labelStack: []
|
|
13870
|
+
};
|
|
13871
|
+
const entry = createBlock(builder);
|
|
13872
|
+
const exit = createBlock(builder);
|
|
13873
|
+
builder.entry = entry;
|
|
13874
|
+
builder.exit = exit;
|
|
13875
|
+
let bodyEnd;
|
|
13876
|
+
if (isNodeOfType(body, "BlockStatement")) bodyEnd = buildStatements(builder, body.body, entry);
|
|
13877
|
+
else {
|
|
13878
|
+
mapDescendantsToBlock(builder, body, entry);
|
|
13879
|
+
bodyEnd = entry;
|
|
13880
|
+
}
|
|
13881
|
+
addEdge(bodyEnd, exit, "uncond");
|
|
13882
|
+
return {
|
|
13883
|
+
owner: functionNode,
|
|
13884
|
+
entry,
|
|
13885
|
+
exit,
|
|
13886
|
+
blocks: builder.blocks,
|
|
13887
|
+
blockOf: (node) => builder.nodeBlock.get(node) ?? null
|
|
13888
|
+
};
|
|
13889
|
+
};
|
|
13890
|
+
const computeUnconditionalSet = (cfg) => {
|
|
13891
|
+
const reachableFromEntry = (excluded) => {
|
|
13892
|
+
const visited = /* @__PURE__ */ new Set();
|
|
13893
|
+
const queue = [];
|
|
13894
|
+
if (cfg.entry !== excluded) queue.push(cfg.entry);
|
|
13895
|
+
while (queue.length > 0) {
|
|
13896
|
+
const block = queue.shift();
|
|
13897
|
+
if (visited.has(block)) continue;
|
|
13898
|
+
visited.add(block);
|
|
13899
|
+
for (const edge of block.successors) {
|
|
13900
|
+
if (edge.kind === "throw") continue;
|
|
13901
|
+
if (edge.to === excluded) continue;
|
|
13902
|
+
queue.push(edge.to);
|
|
13903
|
+
}
|
|
13904
|
+
}
|
|
13905
|
+
return visited;
|
|
13906
|
+
};
|
|
13907
|
+
const reachableFromEntryFull = reachableFromEntry(null);
|
|
13908
|
+
const unconditional = /* @__PURE__ */ new Set();
|
|
13909
|
+
unconditional.add(cfg.entry);
|
|
13910
|
+
unconditional.add(cfg.exit);
|
|
13911
|
+
for (const block of cfg.blocks) {
|
|
13912
|
+
if (unconditional.has(block)) continue;
|
|
13913
|
+
if (!reachableFromEntryFull.has(block)) {
|
|
13914
|
+
unconditional.add(block);
|
|
13915
|
+
continue;
|
|
13916
|
+
}
|
|
13917
|
+
if (!reachableFromEntry(block).has(cfg.exit)) unconditional.add(block);
|
|
13918
|
+
}
|
|
13919
|
+
return unconditional;
|
|
13920
|
+
};
|
|
13921
|
+
const analyzeControlFlow = (program) => {
|
|
13922
|
+
nextBlockId = 0;
|
|
13923
|
+
const functionCfgs = /* @__PURE__ */ new Map();
|
|
13924
|
+
const pendingFunctions = [];
|
|
13925
|
+
const buildFor = (functionNode, body) => {
|
|
13926
|
+
const cfg = buildFunctionCfg(functionNode, body, pendingFunctions);
|
|
13927
|
+
functionCfgs.set(functionNode, {
|
|
13928
|
+
cfg,
|
|
13929
|
+
unconditionalSet: computeUnconditionalSet(cfg)
|
|
13930
|
+
});
|
|
13931
|
+
};
|
|
13932
|
+
if (isNodeOfType(program, "Program")) buildFor(program, {
|
|
13933
|
+
type: "BlockStatement",
|
|
13934
|
+
body: program.body
|
|
13935
|
+
});
|
|
13936
|
+
for (let functionIndex = 0; functionIndex < pendingFunctions.length; functionIndex += 1) {
|
|
13937
|
+
const functionNode = pendingFunctions[functionIndex];
|
|
13938
|
+
if (!isFunctionLike$1(functionNode) || !functionNode.body || functionCfgs.has(functionNode)) continue;
|
|
13939
|
+
buildFor(functionNode, functionNode.body);
|
|
13940
|
+
}
|
|
13941
|
+
const getFunctionEntry = (functionNode) => {
|
|
13942
|
+
const existingEntry = functionCfgs.get(functionNode);
|
|
13943
|
+
if (existingEntry) return existingEntry;
|
|
13944
|
+
if (!isFunctionLike$1(functionNode) || !functionNode.body) return null;
|
|
13945
|
+
buildFor(functionNode, functionNode.body);
|
|
13946
|
+
return functionCfgs.get(functionNode) ?? null;
|
|
13947
|
+
};
|
|
13948
|
+
const enclosingFunction = (node) => {
|
|
13949
|
+
let current = node;
|
|
13950
|
+
while (current) {
|
|
13951
|
+
if (isFunctionLike$1(current)) return current;
|
|
13952
|
+
if (isNodeOfType(current, "Program")) return current;
|
|
13953
|
+
current = current.parent ?? null;
|
|
13954
|
+
}
|
|
13955
|
+
return null;
|
|
13956
|
+
};
|
|
13957
|
+
const cfgFor = (functionLike) => {
|
|
13958
|
+
return getFunctionEntry(functionLike)?.cfg ?? null;
|
|
13959
|
+
};
|
|
13960
|
+
const isUnconditionalFromEntry = (node) => {
|
|
13961
|
+
const owner = enclosingFunction(node);
|
|
13962
|
+
if (!owner) return true;
|
|
13963
|
+
const entry = getFunctionEntry(owner);
|
|
13964
|
+
if (!entry) return true;
|
|
13965
|
+
const block = entry.cfg.blockOf(node);
|
|
13966
|
+
if (!block) return true;
|
|
13967
|
+
return entry.unconditionalSet.has(block);
|
|
13968
|
+
};
|
|
13969
|
+
return {
|
|
13970
|
+
cfgFor,
|
|
13971
|
+
enclosingFunction,
|
|
13972
|
+
isUnconditionalFromEntry
|
|
13973
|
+
};
|
|
13974
|
+
};
|
|
13975
|
+
//#endregion
|
|
13976
|
+
//#region src/plugin/utils/resolve-fresh-render-value.ts
|
|
13977
|
+
const classifyFreshRenderValue = (expression) => {
|
|
13978
|
+
const candidate = stripParenExpression(expression);
|
|
13979
|
+
if (isNodeOfType(candidate, "ObjectExpression")) return "object";
|
|
13980
|
+
if (isNodeOfType(candidate, "ArrayExpression")) return "array";
|
|
13981
|
+
if (isNodeOfType(candidate, "ArrowFunctionExpression") || isNodeOfType(candidate, "FunctionExpression")) return "function";
|
|
13982
|
+
if (isNodeOfType(candidate, "JSXElement") || isNodeOfType(candidate, "JSXFragment")) return "JSX";
|
|
13983
|
+
if (isNodeOfType(candidate, "NewExpression")) return "instance";
|
|
13984
|
+
return null;
|
|
13985
|
+
};
|
|
13986
|
+
const resolveFreshRenderValue = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
13987
|
+
const directKind = classifyFreshRenderValue(expression);
|
|
13988
|
+
if (directKind) return {
|
|
13989
|
+
bindingName: null,
|
|
13990
|
+
kind: directKind
|
|
13991
|
+
};
|
|
13992
|
+
const candidate = stripParenExpression(expression);
|
|
13993
|
+
if (!isNodeOfType(candidate, "Identifier")) return null;
|
|
13994
|
+
const symbol = scopes.symbolFor(candidate);
|
|
13995
|
+
if (!symbol || symbol.scope.kind === "module" || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read") || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
13996
|
+
visitedSymbolIds.add(symbol.id);
|
|
13997
|
+
const resolved = resolveFreshRenderValue(symbol.initializer, scopes, visitedSymbolIds);
|
|
13998
|
+
return resolved ? {
|
|
13999
|
+
bindingName: candidate.name,
|
|
14000
|
+
kind: resolved.kind
|
|
14001
|
+
} : null;
|
|
14002
|
+
};
|
|
14003
|
+
//#endregion
|
|
14004
|
+
//#region src/plugin/utils/find-forwarded-fresh-hook-dependencies.ts
|
|
14005
|
+
const DEPENDENCY_HOOK_NAMES = new Set([
|
|
14006
|
+
...HOOKS_WITH_DEPS,
|
|
14007
|
+
"useImperativeHandle",
|
|
14008
|
+
"useInsertionEffect"
|
|
14009
|
+
]);
|
|
14010
|
+
const crossFileScopes = /* @__PURE__ */ new WeakMap();
|
|
14011
|
+
const crossFileControlFlow = /* @__PURE__ */ new WeakMap();
|
|
14012
|
+
const forwardedFreshDependencyCache = /* @__PURE__ */ new WeakMap();
|
|
14013
|
+
const getCrossFileScopes = (resolved) => {
|
|
14014
|
+
const cached = crossFileScopes.get(resolved.programNode);
|
|
14015
|
+
if (cached) return cached;
|
|
14016
|
+
const scopes = analyzeScopes(resolved.programNode);
|
|
14017
|
+
crossFileScopes.set(resolved.programNode, scopes);
|
|
14018
|
+
return scopes;
|
|
14019
|
+
};
|
|
14020
|
+
const getCrossFileControlFlow = (resolved) => {
|
|
14021
|
+
const cached = crossFileControlFlow.get(resolved.programNode);
|
|
14022
|
+
if (cached) return cached;
|
|
14023
|
+
const cfg = analyzeControlFlow(resolved.programNode);
|
|
14024
|
+
crossFileControlFlow.set(resolved.programNode, cfg);
|
|
14025
|
+
return cfg;
|
|
14026
|
+
};
|
|
14027
|
+
const isNodeReachable = (node, cfg) => {
|
|
14028
|
+
const owner = cfg.enclosingFunction(node);
|
|
14029
|
+
if (!owner) return true;
|
|
14030
|
+
const functionCfg = cfg.cfgFor(owner);
|
|
14031
|
+
if (!functionCfg) return true;
|
|
14032
|
+
const targetBlock = functionCfg.blockOf(node);
|
|
14033
|
+
if (!targetBlock) return true;
|
|
14034
|
+
const visitedBlocks = new Set([functionCfg.entry]);
|
|
14035
|
+
const pendingBlocks = [functionCfg.entry];
|
|
14036
|
+
while (pendingBlocks.length > 0) {
|
|
14037
|
+
const currentBlock = pendingBlocks.pop();
|
|
14038
|
+
if (!currentBlock) break;
|
|
14039
|
+
if (currentBlock === targetBlock) return true;
|
|
14040
|
+
for (const edge of currentBlock.successors) {
|
|
14041
|
+
if (visitedBlocks.has(edge.to)) continue;
|
|
14042
|
+
visitedBlocks.add(edge.to);
|
|
14043
|
+
pendingBlocks.push(edge.to);
|
|
14044
|
+
}
|
|
14045
|
+
}
|
|
14046
|
+
return false;
|
|
14047
|
+
};
|
|
14048
|
+
const isCustomHookFunction = (functionNode, fallbackName) => {
|
|
14049
|
+
const displayName = componentOrHookDisplayNameForFunction(functionNode) ?? fallbackName ?? "";
|
|
14050
|
+
return /^use[A-Z0-9]/.test(displayName);
|
|
14051
|
+
};
|
|
14052
|
+
const getImportedHookBinding = (callee, scopes) => {
|
|
14053
|
+
if (!isNodeOfType(callee, "Identifier")) return null;
|
|
14054
|
+
const importedSymbol = resolveConstIdentifierAlias(callee, scopes);
|
|
14055
|
+
if (importedSymbol?.kind !== "import" || !importedSymbol.initializer) return null;
|
|
14056
|
+
const importDeclaration = importedSymbol.initializer.parent;
|
|
14057
|
+
if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration")) return null;
|
|
14058
|
+
const source = importDeclaration.source?.value;
|
|
14059
|
+
if (typeof source !== "string") return null;
|
|
14060
|
+
const exportedName = resolveImportedExportName(importedSymbol.initializer);
|
|
14061
|
+
return exportedName ? {
|
|
14062
|
+
exportedName,
|
|
14063
|
+
source
|
|
14064
|
+
} : null;
|
|
14065
|
+
};
|
|
14066
|
+
const resolveImportedHookFunction = (callee, scopes, currentFilename) => {
|
|
14067
|
+
if (!currentFilename) return null;
|
|
14068
|
+
const importedBinding = getImportedHookBinding(callee, scopes);
|
|
14069
|
+
if (!importedBinding) return null;
|
|
14070
|
+
const resolved = resolveCrossFileFunctionExportWithFilePath(currentFilename, importedBinding.source, importedBinding.exportedName);
|
|
14071
|
+
if (!resolved || !isCustomHookFunction(resolved.functionNode, importedBinding.exportedName)) return null;
|
|
14072
|
+
return {
|
|
14073
|
+
cfg: getCrossFileControlFlow(resolved),
|
|
14074
|
+
filePath: resolved.filePath,
|
|
14075
|
+
functionNode: resolved.functionNode,
|
|
14076
|
+
programNode: resolved.programNode,
|
|
14077
|
+
scopes: getCrossFileScopes(resolved)
|
|
14078
|
+
};
|
|
14079
|
+
};
|
|
14080
|
+
const dependencyIndexForReactHookReference = (expression, scopes, dependencyHookNames, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14081
|
+
const candidate = stripParenExpression(expression);
|
|
14082
|
+
if (isNodeOfType(candidate, "Identifier")) {
|
|
14083
|
+
const symbol = resolveConstIdentifierAlias(candidate, scopes);
|
|
14084
|
+
if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
|
|
14085
|
+
if (isImportedFromReact(symbol)) {
|
|
14086
|
+
const importedName = getImportedName(symbol.declarationNode);
|
|
14087
|
+
if (!importedName || !dependencyHookNames.has(importedName)) return null;
|
|
14088
|
+
return importedName === "useImperativeHandle" ? 2 : 1;
|
|
14089
|
+
}
|
|
14090
|
+
if (symbol.kind !== "const" || !symbol.initializer || isSymbolMutated(symbol)) return null;
|
|
14091
|
+
visitedSymbolIds.add(symbol.id);
|
|
14092
|
+
return dependencyIndexForReactHookReference(symbol.initializer, scopes, dependencyHookNames, visitedSymbolIds);
|
|
14093
|
+
}
|
|
14094
|
+
if (isNodeOfType(candidate, "MemberExpression")) {
|
|
14095
|
+
const hookName = getStaticPropertyName(candidate);
|
|
14096
|
+
if (!hookName || !dependencyHookNames.has(hookName)) return null;
|
|
14097
|
+
const receiver = stripParenExpression(candidate.object);
|
|
14098
|
+
if (!isNodeOfType(receiver, "Identifier") || !isReactNamespaceImport(receiver, scopes)) return null;
|
|
14099
|
+
return hookName === "useImperativeHandle" ? 2 : 1;
|
|
14100
|
+
}
|
|
14101
|
+
if (!isNodeOfType(candidate, "ConditionalExpression")) return null;
|
|
14102
|
+
const consequentIndex = dependencyIndexForReactHookReference(candidate.consequent, scopes, dependencyHookNames, new Set(visitedSymbolIds));
|
|
14103
|
+
const alternateIndex = dependencyIndexForReactHookReference(candidate.alternate, scopes, dependencyHookNames, new Set(visitedSymbolIds));
|
|
14104
|
+
return consequentIndex !== null && consequentIndex === alternateIndex ? consequentIndex : null;
|
|
14105
|
+
};
|
|
14106
|
+
const getImportedReactDependencyIndex = (callExpression, scopes, currentFilename, dependencyHookNames) => {
|
|
14107
|
+
if (!currentFilename) return null;
|
|
14108
|
+
const importedBinding = getImportedHookBinding(stripParenExpression(callExpression.callee), scopes);
|
|
14109
|
+
if (!importedBinding) return null;
|
|
14110
|
+
const resolved = resolveCrossFileValueExportWithFilePath(currentFilename, importedBinding.source, importedBinding.exportedName);
|
|
14111
|
+
if (!resolved) return null;
|
|
14112
|
+
return dependencyIndexForReactHookReference(resolved.exportedNode, getCrossFileScopes(resolved), dependencyHookNames);
|
|
14113
|
+
};
|
|
14114
|
+
const resolveHookFunction = (callExpression, scopes, cfg, currentFilename) => {
|
|
14115
|
+
const callee = stripParenExpression(callExpression.callee);
|
|
14116
|
+
const localFunction = resolveExactLocalFunction(callee, scopes);
|
|
14117
|
+
if (localFunction && isCustomHookFunction(localFunction)) {
|
|
14118
|
+
const programNode = findProgramRoot(localFunction);
|
|
14119
|
+
if (!programNode) return null;
|
|
14120
|
+
return {
|
|
14121
|
+
cfg,
|
|
14122
|
+
filePath: currentFilename,
|
|
14123
|
+
functionNode: localFunction,
|
|
14124
|
+
programNode,
|
|
14125
|
+
scopes
|
|
14126
|
+
};
|
|
14127
|
+
}
|
|
14128
|
+
return resolveImportedHookFunction(callee, scopes, currentFilename);
|
|
14129
|
+
};
|
|
14130
|
+
const collectParameterBindings = (functionNode) => {
|
|
14131
|
+
if (!isFunctionLike$1(functionNode)) return [];
|
|
14132
|
+
const bindings = [];
|
|
14133
|
+
for (const [parameterIndex, rawParameter] of (functionNode.params ?? []).entries()) {
|
|
14134
|
+
const parameter = stripParenExpression(rawParameter);
|
|
14135
|
+
if (isNodeOfType(parameter, "Identifier")) {
|
|
14136
|
+
bindings.push({
|
|
14137
|
+
bindingIdentifier: parameter,
|
|
14138
|
+
defaultExpression: null,
|
|
14139
|
+
parameterIndex,
|
|
14140
|
+
propertyName: null
|
|
14141
|
+
});
|
|
14142
|
+
continue;
|
|
14143
|
+
}
|
|
14144
|
+
if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) {
|
|
14145
|
+
bindings.push({
|
|
14146
|
+
bindingIdentifier: parameter.left,
|
|
14147
|
+
defaultExpression: parameter.right,
|
|
14148
|
+
parameterIndex,
|
|
14149
|
+
propertyName: null
|
|
14150
|
+
});
|
|
14151
|
+
continue;
|
|
14152
|
+
}
|
|
14153
|
+
if (!isNodeOfType(parameter, "ObjectPattern")) continue;
|
|
14154
|
+
for (const property of parameter.properties ?? []) {
|
|
14155
|
+
if (!isNodeOfType(property, "Property")) continue;
|
|
14156
|
+
const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
14157
|
+
if (!propertyName) continue;
|
|
14158
|
+
const value = stripParenExpression(property.value);
|
|
14159
|
+
if (isNodeOfType(value, "Identifier")) bindings.push({
|
|
14160
|
+
bindingIdentifier: value,
|
|
14161
|
+
defaultExpression: null,
|
|
14162
|
+
parameterIndex,
|
|
14163
|
+
propertyName
|
|
14164
|
+
});
|
|
14165
|
+
else if (isNodeOfType(value, "AssignmentPattern") && isNodeOfType(value.left, "Identifier")) bindings.push({
|
|
14166
|
+
bindingIdentifier: value.left,
|
|
14167
|
+
defaultExpression: value.right,
|
|
14168
|
+
parameterIndex,
|
|
14169
|
+
propertyName
|
|
14170
|
+
});
|
|
14171
|
+
}
|
|
14172
|
+
}
|
|
14173
|
+
return bindings;
|
|
14174
|
+
};
|
|
14175
|
+
const resolveConstObjectExpression = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14176
|
+
const candidate = stripParenExpression(expression);
|
|
14177
|
+
if (isNodeOfType(candidate, "ObjectExpression")) return candidate;
|
|
14178
|
+
if (!isNodeOfType(candidate, "Identifier")) return null;
|
|
14179
|
+
const symbol = scopes.symbolFor(candidate);
|
|
14180
|
+
if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || isSymbolMutated(symbol)) return null;
|
|
14181
|
+
visitedSymbolIds.add(symbol.id);
|
|
14182
|
+
return resolveConstObjectExpression(symbol.initializer, scopes, visitedSymbolIds);
|
|
14183
|
+
};
|
|
14184
|
+
const findObjectPropertyValue = (objectExpression, propertyName) => {
|
|
14185
|
+
for (let propertyIndex = objectExpression.properties.length - 1; propertyIndex >= 0; propertyIndex--) {
|
|
14186
|
+
const property = objectExpression.properties[propertyIndex];
|
|
14187
|
+
if (isNodeOfType(property, "SpreadElement")) return {
|
|
14188
|
+
expression: null,
|
|
14189
|
+
isProvenOmitted: false
|
|
14190
|
+
};
|
|
14191
|
+
if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === propertyName) return {
|
|
14192
|
+
expression: property.value,
|
|
14193
|
+
isProvenOmitted: false
|
|
14194
|
+
};
|
|
14195
|
+
}
|
|
14196
|
+
return {
|
|
14197
|
+
expression: null,
|
|
14198
|
+
isProvenOmitted: true
|
|
14199
|
+
};
|
|
14200
|
+
};
|
|
14201
|
+
const resolveArgumentValue = (callExpression, parameter, callerScopes) => {
|
|
14202
|
+
if ((callExpression.arguments ?? []).slice(0, parameter.parameterIndex + 1).some((argument) => isNodeOfType(argument, "SpreadElement"))) return {
|
|
14203
|
+
expression: null,
|
|
14204
|
+
isProvenOmitted: false
|
|
14205
|
+
};
|
|
14206
|
+
const argument = callExpression.arguments?.[parameter.parameterIndex];
|
|
14207
|
+
if (!argument) return {
|
|
14208
|
+
expression: null,
|
|
14209
|
+
isProvenOmitted: true
|
|
14210
|
+
};
|
|
14211
|
+
if (parameter.propertyName === null) return {
|
|
14212
|
+
expression: argument,
|
|
14213
|
+
isProvenOmitted: false
|
|
14214
|
+
};
|
|
14215
|
+
const objectExpression = resolveConstObjectExpression(argument, callerScopes);
|
|
14216
|
+
if (!objectExpression) return {
|
|
14217
|
+
expression: null,
|
|
14218
|
+
isProvenOmitted: false
|
|
14219
|
+
};
|
|
14220
|
+
return findObjectPropertyValue(objectExpression, parameter.propertyName);
|
|
14221
|
+
};
|
|
14222
|
+
const isSymbolMutated = (symbol) => {
|
|
14223
|
+
if (symbol.references.some((reference) => reference.flag !== "read")) return true;
|
|
14224
|
+
for (const reference of symbol.references) {
|
|
14225
|
+
let expression = reference.identifier;
|
|
14226
|
+
while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) expression = expression.parent;
|
|
14227
|
+
const parent = expression.parent;
|
|
14228
|
+
if (isNodeOfType(parent, "AssignmentExpression") && parent.left === expression || isNodeOfType(parent, "UpdateExpression") && parent.argument === expression || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === expression) return true;
|
|
14229
|
+
if (isNodeOfType(parent, "CallExpression") && parent.callee === expression && isNodeOfType(expression, "MemberExpression")) {
|
|
14230
|
+
const methodName = getStaticPropertyName(expression);
|
|
14231
|
+
if (methodName && (MUTATING_ARRAY_METHODS.has(methodName) || MUTATING_COLLECTION_METHODS.has(methodName))) return true;
|
|
14232
|
+
}
|
|
14233
|
+
}
|
|
14234
|
+
return false;
|
|
14235
|
+
};
|
|
14236
|
+
const expressionHasTaint = (expression, mode, scopes, taint, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14237
|
+
const candidate = stripParenExpression(expression);
|
|
14238
|
+
if (!isNodeOfType(candidate, "Identifier")) return false;
|
|
14239
|
+
const symbol = scopes.symbolFor(candidate);
|
|
14240
|
+
if (!symbol || visitedSymbolIds.has(symbol.id) || isSymbolMutated(symbol)) return false;
|
|
14241
|
+
if ((mode === "value" ? taint.valueSymbolIds : taint.listSymbolIds).has(symbol.id)) return true;
|
|
14242
|
+
if (symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
|
|
14243
|
+
visitedSymbolIds.add(symbol.id);
|
|
14244
|
+
return expressionHasTaint(symbol.initializer, mode, scopes, taint, visitedSymbolIds);
|
|
14245
|
+
};
|
|
14246
|
+
const dependencyListContainsTaint = (expression, scopes, taint, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14247
|
+
if (expressionHasTaint(expression, "list", scopes, taint)) return true;
|
|
14248
|
+
const candidate = stripParenExpression(expression);
|
|
14249
|
+
if (isNodeOfType(candidate, "Identifier")) {
|
|
14250
|
+
const symbol = scopes.symbolFor(candidate);
|
|
14251
|
+
if (!symbol || visitedSymbolIds.has(symbol.id) || symbol.kind !== "const" || !symbol.initializer || isSymbolMutated(symbol)) return false;
|
|
14252
|
+
visitedSymbolIds.add(symbol.id);
|
|
14253
|
+
return dependencyListContainsTaint(symbol.initializer, scopes, taint, visitedSymbolIds);
|
|
14254
|
+
}
|
|
14255
|
+
if (!isNodeOfType(candidate, "ArrayExpression")) return false;
|
|
14256
|
+
return (candidate.elements ?? []).some((element) => {
|
|
14257
|
+
if (!element) return false;
|
|
14258
|
+
if (isNodeOfType(element, "SpreadElement")) return expressionHasTaint(element.argument, "list", scopes, taint);
|
|
14259
|
+
return expressionHasTaint(element, "value", scopes, taint);
|
|
14260
|
+
});
|
|
14261
|
+
};
|
|
14262
|
+
const buildForwardedTaint = (callExpression, target, callerScopes, callerTaint) => {
|
|
14263
|
+
const forwardedTaint = {
|
|
14264
|
+
listSymbolIds: /* @__PURE__ */ new Set(),
|
|
14265
|
+
valueSymbolIds: /* @__PURE__ */ new Set()
|
|
14266
|
+
};
|
|
14267
|
+
for (const parameter of collectParameterBindings(target.functionNode)) {
|
|
14268
|
+
const argument = resolveArgumentValue(callExpression, parameter, callerScopes);
|
|
14269
|
+
if (!argument.expression) continue;
|
|
14270
|
+
const targetSymbol = target.scopes.symbolFor(parameter.bindingIdentifier);
|
|
14271
|
+
if (!targetSymbol || isSymbolMutated(targetSymbol)) continue;
|
|
14272
|
+
if (expressionHasTaint(argument.expression, "value", callerScopes, callerTaint)) forwardedTaint.valueSymbolIds.add(targetSymbol.id);
|
|
14273
|
+
if (expressionHasTaint(argument.expression, "list", callerScopes, callerTaint) || dependencyListContainsTaint(argument.expression, callerScopes, callerTaint)) forwardedTaint.listSymbolIds.add(targetSymbol.id);
|
|
14274
|
+
}
|
|
14275
|
+
return forwardedTaint;
|
|
14276
|
+
};
|
|
14277
|
+
const taintReachesBuiltInDependency = (target, taint, remainingDepth, visitedFunctions, dependencyHookNames) => {
|
|
14278
|
+
if (!isFunctionLike$1(target.functionNode)) return false;
|
|
14279
|
+
const functionNode = target.functionNode;
|
|
14280
|
+
if (remainingDepth < 0 || visitedFunctions.has(functionNode) || [...taint.valueSymbolIds, ...taint.listSymbolIds].some((symbolId) => {
|
|
14281
|
+
const symbol = target.scopes.ownScopeFor(target.functionNode)?.symbols.find((candidate) => candidate.id === symbolId);
|
|
14282
|
+
return symbol ? isSymbolMutated(symbol) : false;
|
|
14283
|
+
})) return false;
|
|
14284
|
+
const nextVisitedFunctions = new Set(visitedFunctions);
|
|
14285
|
+
nextVisitedFunctions.add(functionNode);
|
|
14286
|
+
let didReachDependency = false;
|
|
14287
|
+
walkAst(functionNode.body, (node) => {
|
|
14288
|
+
if (didReachDependency) return false;
|
|
14289
|
+
if (node !== functionNode.body && isFunctionLike$1(node)) return false;
|
|
14290
|
+
if (!isNodeOfType(node, "CallExpression")) return;
|
|
14291
|
+
if (!isNodeReachable(node, target.cfg)) return;
|
|
14292
|
+
if (isReactApiCall(node, dependencyHookNames, target.scopes, { allowGlobalReactNamespace: true })) {
|
|
14293
|
+
const callee = stripParenExpression(node.callee);
|
|
14294
|
+
const dependencyIndex = (isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : null) === "useImperativeHandle" ? 2 : 1;
|
|
14295
|
+
const dependencies = node.arguments?.[dependencyIndex];
|
|
14296
|
+
if (dependencies && !isNodeOfType(dependencies, "SpreadElement") && dependencyListContainsTaint(dependencies, target.scopes, taint)) {
|
|
14297
|
+
didReachDependency = true;
|
|
14298
|
+
return false;
|
|
14299
|
+
}
|
|
14300
|
+
return;
|
|
14301
|
+
}
|
|
14302
|
+
const importedDependencyIndex = getImportedReactDependencyIndex(node, target.scopes, target.filePath, dependencyHookNames);
|
|
14303
|
+
if (importedDependencyIndex !== null) {
|
|
14304
|
+
const dependencies = node.arguments?.[importedDependencyIndex];
|
|
14305
|
+
if (dependencies && !isNodeOfType(dependencies, "SpreadElement") && dependencyListContainsTaint(dependencies, target.scopes, taint)) {
|
|
14306
|
+
didReachDependency = true;
|
|
14307
|
+
return false;
|
|
14308
|
+
}
|
|
14309
|
+
return;
|
|
14310
|
+
}
|
|
14311
|
+
if (remainingDepth === 0) return;
|
|
14312
|
+
const nestedTarget = resolveHookFunction(node, target.scopes, target.cfg, target.filePath);
|
|
14313
|
+
if (!nestedTarget) return;
|
|
14314
|
+
const forwardedTaint = buildForwardedTaint(node, nestedTarget, target.scopes, taint);
|
|
14315
|
+
if (forwardedTaint.valueSymbolIds.size === 0 && forwardedTaint.listSymbolIds.size === 0) return;
|
|
14316
|
+
if (taintReachesBuiltInDependency(nestedTarget, forwardedTaint, remainingDepth - 1, nextVisitedFunctions, dependencyHookNames)) {
|
|
14317
|
+
didReachDependency = true;
|
|
14318
|
+
return false;
|
|
14319
|
+
}
|
|
14320
|
+
});
|
|
14321
|
+
return didReachDependency;
|
|
14322
|
+
};
|
|
14323
|
+
const findForwardedFreshHookDependencies = (callExpression, context, dependencyHookNames = DEPENDENCY_HOOK_NAMES) => {
|
|
14324
|
+
const cacheKey = [...dependencyHookNames].sort().join("\0");
|
|
14325
|
+
const cacheForCall = forwardedFreshDependencyCache.get(callExpression);
|
|
14326
|
+
const cached = cacheForCall?.get(cacheKey);
|
|
14327
|
+
if (cached) return cached;
|
|
14328
|
+
const findings = [];
|
|
14329
|
+
if (cacheForCall) cacheForCall.set(cacheKey, findings);
|
|
14330
|
+
else forwardedFreshDependencyCache.set(callExpression, new Map([[cacheKey, findings]]));
|
|
14331
|
+
if (!findRenderPhaseComponentOrHook(callExpression, context.scopes)) return findings;
|
|
14332
|
+
if (!isNodeReachable(callExpression, context.cfg)) return findings;
|
|
14333
|
+
const target = resolveHookFunction(callExpression, context.scopes, context.cfg, context.filename);
|
|
14334
|
+
if (!target) return findings;
|
|
14335
|
+
for (const parameter of collectParameterBindings(target.functionNode)) {
|
|
14336
|
+
const targetSymbol = target.scopes.symbolFor(parameter.bindingIdentifier);
|
|
14337
|
+
if (!targetSymbol || isSymbolMutated(targetSymbol)) continue;
|
|
14338
|
+
const argument = resolveArgumentValue(callExpression, parameter, context.scopes);
|
|
14339
|
+
const sourceExpression = argument.expression ?? (argument.isProvenOmitted ? parameter.defaultExpression : null);
|
|
14340
|
+
if (!sourceExpression) continue;
|
|
14341
|
+
const freshness = resolveFreshRenderValue(sourceExpression, argument.expression ? context.scopes : target.scopes);
|
|
14342
|
+
if (!freshness) continue;
|
|
14343
|
+
if (!taintReachesBuiltInDependency(target, {
|
|
14344
|
+
listSymbolIds: /* @__PURE__ */ new Set(),
|
|
14345
|
+
valueSymbolIds: new Set([targetSymbol.id])
|
|
14346
|
+
}, 4, /* @__PURE__ */ new Set(), dependencyHookNames)) continue;
|
|
14347
|
+
findings.push({
|
|
14348
|
+
bindingName: freshness.bindingName ?? (isNodeOfType(parameter.bindingIdentifier, "Identifier") ? parameter.bindingIdentifier.name : "dependency"),
|
|
14349
|
+
kind: freshness.kind,
|
|
14350
|
+
origin: argument.expression ? "argument" : "default",
|
|
14351
|
+
reportNode: argument.expression ?? callExpression
|
|
14352
|
+
});
|
|
14353
|
+
}
|
|
14354
|
+
return findings;
|
|
14355
|
+
};
|
|
14356
|
+
//#endregion
|
|
13594
14357
|
//#region src/plugin/utils/is-react-hoc-callback-argument.ts
|
|
13595
14358
|
const reactHocCalleeName = (callee) => {
|
|
13596
14359
|
if (isNodeOfType(callee, "Identifier")) return callee.name;
|
|
@@ -13605,6 +14368,22 @@ const isReactHocCallbackArgument = (functionNode) => {
|
|
|
13605
14368
|
return calleeName !== null && REACT_HOC_NAMES.has(calleeName);
|
|
13606
14369
|
};
|
|
13607
14370
|
//#endregion
|
|
14371
|
+
//#region src/plugin/utils/is-outside-all-functions.ts
|
|
14372
|
+
const FUNCTION_SCOPE_KINDS = new Set([
|
|
14373
|
+
"function",
|
|
14374
|
+
"arrow-function",
|
|
14375
|
+
"method"
|
|
14376
|
+
]);
|
|
14377
|
+
const isOutsideAllFunctions = (symbol) => {
|
|
14378
|
+
let scope = symbol.scope;
|
|
14379
|
+
while (scope) {
|
|
14380
|
+
if (FUNCTION_SCOPE_KINDS.has(scope.kind)) return false;
|
|
14381
|
+
if (scope.kind === "module") return true;
|
|
14382
|
+
scope = scope.parent;
|
|
14383
|
+
}
|
|
14384
|
+
return true;
|
|
14385
|
+
};
|
|
14386
|
+
//#endregion
|
|
13608
14387
|
//#region src/plugin/rules/react-builtins/exhaustive-deps-low-level.ts
|
|
13609
14388
|
/**
|
|
13610
14389
|
* Lowest-level helpers consumed by both the main `exhaustive-deps`
|
|
@@ -13657,25 +14436,6 @@ const getHookName = (callee, scopes) => {
|
|
|
13657
14436
|
if (isNodeOfType(strippedCallee, "MemberExpression") && !strippedCallee.computed && isNodeOfType(strippedCallee.property, "Identifier")) return strippedCallee.property.name;
|
|
13658
14437
|
return null;
|
|
13659
14438
|
};
|
|
13660
|
-
const FUNCTION_SCOPE_KINDS = new Set([
|
|
13661
|
-
"function",
|
|
13662
|
-
"arrow-function",
|
|
13663
|
-
"method"
|
|
13664
|
-
]);
|
|
13665
|
-
/**
|
|
13666
|
-
* True for symbols declared at module scope (outside any function
|
|
13667
|
-
* scope). Module-scope bindings don't change between renders so they
|
|
13668
|
-
* don't need to live in dependency arrays.
|
|
13669
|
-
*/
|
|
13670
|
-
const isOutsideAllFunctions = (symbol) => {
|
|
13671
|
-
let scope = symbol.scope;
|
|
13672
|
-
while (scope) {
|
|
13673
|
-
if (FUNCTION_SCOPE_KINDS.has(scope.kind)) return false;
|
|
13674
|
-
if (scope.kind === "module") return true;
|
|
13675
|
-
scope = scope.parent ?? null;
|
|
13676
|
-
}
|
|
13677
|
-
return true;
|
|
13678
|
-
};
|
|
13679
14439
|
//#endregion
|
|
13680
14440
|
//#region src/plugin/rules/react-builtins/exhaustive-deps-messages.ts
|
|
13681
14441
|
const buildMissingDepMessage = (hookName, depName) => `\`${hookName}\` can run with a stale \`${depName}\` & show your users old data.`;
|
|
@@ -13693,6 +14453,7 @@ const buildComplexDepMessage = (hookName) => `A complex expression in \`${hookNa
|
|
|
13693
14453
|
const buildAsyncEffectMessage = (hookName) => `\`${hookName}\` was given an async function, so its cleanup breaks.`;
|
|
13694
14454
|
const buildUnknownCallbackMessage = (hookName) => `\`${hookName}\`'s callback is defined elsewhere, so dependencies can't be checked and stale values can slip through.`;
|
|
13695
14455
|
const buildUnstableDepMessage = (hookName, depName) => `\`${depName}\` is rebuilt every render, so \`${hookName}\` runs every time.`;
|
|
14456
|
+
const buildForwardedUnstableDepMessage = (depName) => `\`${depName}\` is rebuilt every render and reaches a Hook dependency inside this custom Hook.`;
|
|
13696
14457
|
const buildSetStateWithoutDepsMessage = (hookName, setterName) => `\`${hookName}\` calls \`${setterName}\` with no dependency array, so it can loop forever & freeze the component.`;
|
|
13697
14458
|
const buildRefCleanupMessage = (depName) => `Your cleanup may read the wrong node since the ref \`${depName}\` can change before it runs.`;
|
|
13698
14459
|
const buildAssignmentMessage = (name) => `Assigning to \`${name}\` inside a hook is thrown away after each render, so the next render reads the old value.`;
|
|
@@ -14784,6 +15545,10 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
14784
15545
|
};
|
|
14785
15546
|
const isAutoDependenciesHook = (hookName) => settings.experimental_autoDependenciesHooks.includes(hookName);
|
|
14786
15547
|
return { CallExpression(node) {
|
|
15548
|
+
for (const finding of findForwardedFreshHookDependencies(node, context, EFFECT_HOOK_NAMES$1)) context.report({
|
|
15549
|
+
node: finding.reportNode,
|
|
15550
|
+
message: buildForwardedUnstableDepMessage(finding.bindingName)
|
|
15551
|
+
});
|
|
14787
15552
|
const hookName = getHookName(node.callee, context.scopes);
|
|
14788
15553
|
if (!hookName || !isHookOfInterest(hookName, node.callee)) return;
|
|
14789
15554
|
if (HOOKS_REQUIRING_DEPS_MATCH.has(hookName) && !isReactApiCall(node, HOOKS_REQUIRING_DEPS_MATCH, context.scopes, {
|
|
@@ -16659,7 +17424,7 @@ const classifyReturnedExpression = (expression) => {
|
|
|
16659
17424
|
const collectTopLevelReturnExpressions$1 = (block) => {
|
|
16660
17425
|
const returns = [];
|
|
16661
17426
|
walkAst(block, (child) => {
|
|
16662
|
-
if (isFunctionLike$
|
|
17427
|
+
if (isFunctionLike$1(child)) return false;
|
|
16663
17428
|
if (isNodeOfType(child, "ReturnStatement")) returns.push(child.argument);
|
|
16664
17429
|
});
|
|
16665
17430
|
return returns;
|
|
@@ -16684,7 +17449,7 @@ const functionBodyReferencesGetParameter = (fn, getParameterName) => {
|
|
|
16684
17449
|
let found = false;
|
|
16685
17450
|
walkAst(body, (child) => {
|
|
16686
17451
|
if (found) return false;
|
|
16687
|
-
if (isFunctionLike$
|
|
17452
|
+
if (isFunctionLike$1(child) && child !== fn) return false;
|
|
16688
17453
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
16689
17454
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
16690
17455
|
if (child.callee.name === getParameterName) {
|
|
@@ -16845,7 +17610,7 @@ const jotaiSelectAtomInRenderBody = defineRule({
|
|
|
16845
17610
|
if (isBindingUsedAsHandler(nearestFunctionLike)) return;
|
|
16846
17611
|
let outerCursor = nearestFunctionLike;
|
|
16847
17612
|
while (outerCursor) {
|
|
16848
|
-
if (isFunctionLike$
|
|
17613
|
+
if (isFunctionLike$1(outerCursor) && containingFunctionIsComponentOrHook(outerCursor)) {
|
|
16849
17614
|
context.report({
|
|
16850
17615
|
node,
|
|
16851
17616
|
message: "`selectAtom(...)` runs in a component or hook without `useMemo`, so every render makes a new atom & re-subscribes forever, freezing the page for your users. Lift it to module scope, or wrap it in `useMemo(() => selectAtom(...), [deps])`."
|
|
@@ -17181,7 +17946,7 @@ const findEnclosingPerIterationBody = (node) => {
|
|
|
17181
17946
|
let current = node.parent;
|
|
17182
17947
|
while (current) {
|
|
17183
17948
|
if (isNodeOfType(current, "ForStatement") || isNodeOfType(current, "ForInStatement") || isNodeOfType(current, "ForOfStatement") || isNodeOfType(current, "WhileStatement") || isNodeOfType(current, "DoWhileStatement")) return current.body ?? null;
|
|
17184
|
-
if (isFunctionLike$
|
|
17949
|
+
if (isFunctionLike$1(current)) {
|
|
17185
17950
|
if (isIteratorCallback$1(current)) return current.body ?? null;
|
|
17186
17951
|
return null;
|
|
17187
17952
|
}
|
|
@@ -17222,7 +17987,7 @@ const scanPerIterationLayoutReads = (body) => {
|
|
|
17222
17987
|
let hasUsedLayoutRead = false;
|
|
17223
17988
|
let hasDeliberateForcedReflow = false;
|
|
17224
17989
|
walkAst(body, (child) => {
|
|
17225
|
-
const isNestedFunction = child !== body && isFunctionLike$
|
|
17990
|
+
const isNestedFunction = child !== body && isFunctionLike$1(child);
|
|
17226
17991
|
const valueNode = getLayoutReadValueNode(child);
|
|
17227
17992
|
if (valueNode) {
|
|
17228
17993
|
if (isDiscardedValue(valueNode)) hasDeliberateForcedReflow = true;
|
|
@@ -17347,7 +18112,7 @@ const buildMemberAccessKey = (node) => {
|
|
|
17347
18112
|
const isNameShadowedByEnclosingFunctionParameter = (node, name, boundary) => {
|
|
17348
18113
|
let ancestor = node.parent;
|
|
17349
18114
|
while (ancestor && ancestor !== boundary) {
|
|
17350
|
-
if (isFunctionLike$
|
|
18115
|
+
if (isFunctionLike$1(ancestor)) {
|
|
17351
18116
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
17352
18117
|
for (const parameter of ancestor.params ?? []) collectPatternNames(parameter, parameterNames);
|
|
17353
18118
|
if (parameterNames.has(name)) return true;
|
|
@@ -17840,7 +18605,7 @@ const isInsideCacheMemo = (node) => {
|
|
|
17840
18605
|
let child = node;
|
|
17841
18606
|
let cursor = node.parent ?? null;
|
|
17842
18607
|
while (cursor) {
|
|
17843
|
-
if (isFunctionLike$
|
|
18608
|
+
if (isFunctionLike$1(cursor)) return false;
|
|
17844
18609
|
if (isNodeOfType(cursor, "AssignmentExpression") && cursor.right === child) {
|
|
17845
18610
|
if (MEMO_ASSIGNMENT_OPERATORS.has(cursor.operator)) return true;
|
|
17846
18611
|
if (cursor.operator === "=" && isNodeOfType(cursor.left, "Identifier") && isIdentifierInitializedFromCacheLookup(cursor.left)) return true;
|
|
@@ -17859,7 +18624,7 @@ const isDiscardedProbeInsideTry = (node) => {
|
|
|
17859
18624
|
if (!isNodeOfType(statement, "ExpressionStatement")) return false;
|
|
17860
18625
|
let child = statement;
|
|
17861
18626
|
let cursor = statement.parent ?? null;
|
|
17862
|
-
while (cursor && !isFunctionLike$
|
|
18627
|
+
while (cursor && !isFunctionLike$1(cursor)) {
|
|
17863
18628
|
if (isNodeOfType(cursor, "TryStatement") && cursor.block === child) return true;
|
|
17864
18629
|
child = cursor;
|
|
17865
18630
|
cursor = cursor.parent ?? null;
|
|
@@ -17875,7 +18640,7 @@ const getFunctionName = (functionNode) => {
|
|
|
17875
18640
|
};
|
|
17876
18641
|
const isUncacheableOptionsMergeUtility = (node) => {
|
|
17877
18642
|
const enclosingFunction = findEnclosingFunction$1(node);
|
|
17878
|
-
if (!enclosingFunction || !isFunctionLike$
|
|
18643
|
+
if (!enclosingFunction || !isFunctionLike$1(enclosingFunction)) return false;
|
|
17879
18644
|
const functionName = getFunctionName(enclosingFunction);
|
|
17880
18645
|
if (!functionName || isComponentOrHookName(functionName)) return false;
|
|
17881
18646
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
@@ -17903,7 +18668,7 @@ const jsHoistIntl = defineRule({
|
|
|
17903
18668
|
let cursor = node.parent ?? null;
|
|
17904
18669
|
let inFunctionBody = false;
|
|
17905
18670
|
while (cursor) {
|
|
17906
|
-
if (isFunctionLike$
|
|
18671
|
+
if (isFunctionLike$1(cursor)) {
|
|
17907
18672
|
inFunctionBody = true;
|
|
17908
18673
|
const fnParent = cursor.parent;
|
|
17909
18674
|
if (fnParent && isNodeOfType(fnParent, "CallExpression") && fnParent.arguments?.[0] === cursor) {
|
|
@@ -18250,7 +19015,7 @@ const getLoopBoundNames = (loop) => {
|
|
|
18250
19015
|
if (isNodeOfType(child, "Identifier")) names.add(child.name);
|
|
18251
19016
|
});
|
|
18252
19017
|
walkAst(loop, (child) => {
|
|
18253
|
-
if (child !== loop && isFunctionLike$
|
|
19018
|
+
if (child !== loop && isFunctionLike$1(child)) return false;
|
|
18254
19019
|
if (isNodeOfType(child, "VariableDeclarator") && child.id) {
|
|
18255
19020
|
walkAst(child.id, (idNode) => {
|
|
18256
19021
|
if (isNodeOfType(idNode, "Identifier")) names.add(idNode.name);
|
|
@@ -18500,7 +19265,7 @@ const hasDominatingLengthGuard = (callNode, receiverArray, indexedArray) => {
|
|
|
18500
19265
|
let child = callNode;
|
|
18501
19266
|
let ancestor = callNode.parent;
|
|
18502
19267
|
while (ancestor) {
|
|
18503
|
-
if (isFunctionLike$
|
|
19268
|
+
if (isFunctionLike$1(ancestor)) {
|
|
18504
19269
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
18505
19270
|
for (const parameter of ancestor.params ?? []) collectPatternNames(parameter, parameterNames);
|
|
18506
19271
|
if (comparedRootNames.some((rootName) => parameterNames.has(rootName))) return false;
|
|
@@ -18583,7 +19348,7 @@ const findConstInitializer = (identifierName, fromNode) => {
|
|
|
18583
19348
|
let child = fromNode;
|
|
18584
19349
|
let ancestor = fromNode.parent;
|
|
18585
19350
|
while (ancestor) {
|
|
18586
|
-
if (isFunctionLike$
|
|
19351
|
+
if (isFunctionLike$1(ancestor)) {
|
|
18587
19352
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
18588
19353
|
for (const parameter of ancestor.params ?? []) collectPatternNames(parameter, parameterNames);
|
|
18589
19354
|
if (parameterNames.has(identifierName)) return null;
|
|
@@ -18619,7 +19384,7 @@ const PREFIX_INTENT_NAME_PATTERN = /prefix|startswith/i;
|
|
|
18619
19384
|
const isInsidePrefixNamedFunction = (node) => {
|
|
18620
19385
|
let ancestor = node.parent;
|
|
18621
19386
|
while (ancestor) {
|
|
18622
|
-
if (isFunctionLike$
|
|
19387
|
+
if (isFunctionLike$1(ancestor)) {
|
|
18623
19388
|
if (isNodeOfType(ancestor, "FunctionDeclaration") && isNodeOfType(ancestor.id, "Identifier") && PREFIX_INTENT_NAME_PATTERN.test(ancestor.id.name)) return true;
|
|
18624
19389
|
const holder = ancestor.parent;
|
|
18625
19390
|
if (holder && isNodeOfType(holder, "VariableDeclarator") && isNodeOfType(holder.id, "Identifier") && PREFIX_INTENT_NAME_PATTERN.test(holder.id.name)) return true;
|
|
@@ -19348,7 +20113,7 @@ const hasWriteBeforeQuery = (body, identifier) => {
|
|
|
19348
20113
|
if (hasEarlierWrite) return false;
|
|
19349
20114
|
const childStart = getRangeStart(child);
|
|
19350
20115
|
if (childStart !== null && childStart >= queryStart) return false;
|
|
19351
|
-
if (child !== body && isFunctionLike$
|
|
20116
|
+
if (child !== body && isFunctionLike$1(child)) return false;
|
|
19352
20117
|
const writeTarget = isNodeOfType(child, "AssignmentExpression") ? child.left : isNodeOfType(child, "UpdateExpression") ? child.argument : isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement") ? child.left : null;
|
|
19353
20118
|
if (writeTarget && writeTargetContainsIdentifierBinding(writeTarget, identifier)) {
|
|
19354
20119
|
hasEarlierWrite = true;
|
|
@@ -19361,7 +20126,7 @@ const isProtectedByRelationalLoopGuard = (identifier) => {
|
|
|
19361
20126
|
let descendant = identifier;
|
|
19362
20127
|
let ancestor = identifier.parent;
|
|
19363
20128
|
while (ancestor) {
|
|
19364
|
-
if (isFunctionLike$
|
|
20129
|
+
if (isFunctionLike$1(ancestor)) return false;
|
|
19365
20130
|
if ((isNodeOfType(ancestor, "ForStatement") || isNodeOfType(ancestor, "WhileStatement")) && ancestor.body === descendant && testProvesIdentifierIsNotNaN(ancestor.test, identifier)) return !hasWriteBeforeQuery(ancestor.body, identifier);
|
|
19366
20131
|
descendant = ancestor;
|
|
19367
20132
|
ancestor = ancestor.parent;
|
|
@@ -19405,7 +20170,7 @@ const isKnownUserlandMembershipReceiver = (receiver, methodName) => {
|
|
|
19405
20170
|
const findTypeParameter = (reference, typeName) => {
|
|
19406
20171
|
let ancestor = reference.parent;
|
|
19407
20172
|
while (ancestor) {
|
|
19408
|
-
if (isFunctionLike$
|
|
20173
|
+
if (isFunctionLike$1(ancestor) || isNodeOfType(ancestor, "ClassDeclaration") || isNodeOfType(ancestor, "ClassExpression")) {
|
|
19409
20174
|
const matchingTypeParameter = ancestor.typeParameters?.params?.find((typeParameter) => isNodeOfType(typeParameter, "TSTypeParameter") && isNodeOfType(typeParameter.name, "Identifier") && typeParameter.name.name === typeName);
|
|
19410
20175
|
if (matchingTypeParameter && isNodeOfType(matchingTypeParameter, "TSTypeParameter")) return matchingTypeParameter;
|
|
19411
20176
|
}
|
|
@@ -19756,7 +20521,7 @@ const hasSizeReadOnSameExpression = (spreadArgument) => {
|
|
|
19756
20521
|
let ancestor = spreadArgument.parent;
|
|
19757
20522
|
while (ancestor) {
|
|
19758
20523
|
scopeOwner = ancestor;
|
|
19759
|
-
if (isFunctionLike$
|
|
20524
|
+
if (isFunctionLike$1(ancestor)) break;
|
|
19760
20525
|
ancestor = ancestor.parent ?? null;
|
|
19761
20526
|
}
|
|
19762
20527
|
let didFindSizeRead = false;
|
|
@@ -21208,7 +21973,7 @@ const unwrapReactHocFunction = (node, scopes) => resolveReactHocFunction(node, s
|
|
|
21208
21973
|
//#endregion
|
|
21209
21974
|
//#region src/plugin/utils/build-same-file-jsx-slot-prop-registry.ts
|
|
21210
21975
|
const REACT_SLOT_TYPE_NAMES = new Set(["ReactNode", "ReactElement"]);
|
|
21211
|
-
const unwrapTopLevelDeclaration = (statement) => {
|
|
21976
|
+
const unwrapTopLevelDeclaration$1 = (statement) => {
|
|
21212
21977
|
if (isNodeOfType(statement, "ExportNamedDeclaration")) return statement.declaration;
|
|
21213
21978
|
if (isNodeOfType(statement, "ExportDefaultDeclaration")) return statement.declaration;
|
|
21214
21979
|
return statement;
|
|
@@ -21245,7 +22010,7 @@ const buildReactSlotTypeEnvironment = (program) => {
|
|
|
21245
22010
|
}
|
|
21246
22011
|
continue;
|
|
21247
22012
|
}
|
|
21248
|
-
const declaration = unwrapTopLevelDeclaration(statement);
|
|
22013
|
+
const declaration = unwrapTopLevelDeclaration$1(statement);
|
|
21249
22014
|
if (!declaration) continue;
|
|
21250
22015
|
if ((isNodeOfType(declaration, "TSInterfaceDeclaration") || isNodeOfType(declaration, "TSTypeAliasDeclaration")) && isNodeOfType(declaration.id, "Identifier")) sameFileTypeDeclarations.set(declaration.id.name, declaration);
|
|
21251
22016
|
if (isNodeOfType(declaration, "TSModuleDeclaration") && isNodeOfType(declaration.id, "Identifier") && declaration.id.name === "JSX") hasLocalJsxNamespace = true;
|
|
@@ -21337,7 +22102,7 @@ const buildSameFileJsxSlotPropRegistry = (program, memoRegistry, scopes) => {
|
|
|
21337
22102
|
if (!isNodeOfType(program, "Program")) return registry;
|
|
21338
22103
|
const environment = buildReactSlotTypeEnvironment(program);
|
|
21339
22104
|
for (const statement of program.body) {
|
|
21340
|
-
const declaration = unwrapTopLevelDeclaration(statement);
|
|
22105
|
+
const declaration = unwrapTopLevelDeclaration$1(statement);
|
|
21341
22106
|
if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) continue;
|
|
21342
22107
|
for (const declarator of declaration.declarations) {
|
|
21343
22108
|
if (!isNodeOfType(declarator.id, "Identifier") || memoRegistry.get(declarator.id.name) !== "memoised" || !declarator.init) continue;
|
|
@@ -24864,7 +25629,7 @@ const containsFetchCall = (node, options) => {
|
|
|
24864
25629
|
let didFindFetchCall = false;
|
|
24865
25630
|
walkAst(node, (child) => {
|
|
24866
25631
|
if (didFindFetchCall) return false;
|
|
24867
|
-
if (effectInvokedFunctions && child !== node && isFunctionLike$
|
|
25632
|
+
if (effectInvokedFunctions && child !== node && isFunctionLike$1(child) && !effectInvokedFunctions.has(child)) return false;
|
|
24868
25633
|
if (isFetchCall$1(child)) {
|
|
24869
25634
|
didFindFetchCall = true;
|
|
24870
25635
|
return false;
|
|
@@ -24962,7 +25727,7 @@ const isInsidePollingLoop = (navigationNode, effectCallback, timerScheduledNames
|
|
|
24962
25727
|
if (timerScheduledNames.size === 0) return false;
|
|
24963
25728
|
let cursor = navigationNode.parent;
|
|
24964
25729
|
while (cursor && cursor !== effectCallback) {
|
|
24965
|
-
if (isFunctionLike$
|
|
25730
|
+
if (isFunctionLike$1(cursor)) {
|
|
24966
25731
|
const bindingName = getFunctionBindingName(cursor);
|
|
24967
25732
|
if (bindingName && timerScheduledNames.has(bindingName)) return true;
|
|
24968
25733
|
}
|
|
@@ -25002,7 +25767,7 @@ const nextjsNoClientSideRedirect = defineRule({
|
|
|
25002
25767
|
const effectInvokedFunctions = collectEffectInvokedFunctions(callback);
|
|
25003
25768
|
const timerScheduledNames = collectTimerScheduledFunctionNames(callback);
|
|
25004
25769
|
walkAst(callback, (child) => {
|
|
25005
|
-
if (child !== callback && isFunctionLike$
|
|
25770
|
+
if (child !== callback && isFunctionLike$1(child) && !effectInvokedFunctions.has(child)) return false;
|
|
25006
25771
|
const navigationDescription = describeClientSideNavigation(child);
|
|
25007
25772
|
if (navigationDescription) {
|
|
25008
25773
|
if (isNodeOfType(child, "CallExpression")) {
|
|
@@ -25392,7 +26157,7 @@ const catchClauseRethrowsCaught = (handler) => {
|
|
|
25392
26157
|
let didRethrow = false;
|
|
25393
26158
|
walkAst(handler.body, (child) => {
|
|
25394
26159
|
if (didRethrow) return false;
|
|
25395
|
-
if (child !== handler.body && isFunctionLike$
|
|
26160
|
+
if (child !== handler.body && isFunctionLike$1(child)) return false;
|
|
25396
26161
|
if (isNodeOfType(child, "ThrowStatement") && isNodeOfType(child.argument, "Identifier") && child.argument.name === caughtBindingName && doesThrowEscapeCatchClause(child, handler)) {
|
|
25397
26162
|
didRethrow = true;
|
|
25398
26163
|
return false;
|
|
@@ -25417,7 +26182,7 @@ const findGuardingTryStatement = (node) => {
|
|
|
25417
26182
|
let child = node;
|
|
25418
26183
|
let ancestor = node.parent;
|
|
25419
26184
|
while (ancestor) {
|
|
25420
|
-
if (isFunctionLike$
|
|
26185
|
+
if (isFunctionLike$1(ancestor) && !isImmediatelyInvokedFunction(ancestor)) return null;
|
|
25421
26186
|
if (isNodeOfType(ancestor, "TryStatement") && ancestor.block === child && ancestor.handler && !catchClauseRethrowsCaught(ancestor.handler)) return ancestor;
|
|
25422
26187
|
child = ancestor;
|
|
25423
26188
|
ancestor = ancestor.parent ?? null;
|
|
@@ -25716,7 +26481,7 @@ const collectChainedGetHandlerBodies = (initNode) => {
|
|
|
25716
26481
|
};
|
|
25717
26482
|
const resolveBodiesFromExpression = (expression, resolveBinding, remainingDepth) => {
|
|
25718
26483
|
if (remainingDepth <= 0) return [];
|
|
25719
|
-
if (isFunctionLike$
|
|
26484
|
+
if (isFunctionLike$1(expression)) return expression.body ? [expression.body] : [];
|
|
25720
26485
|
if (isNodeOfType(expression, "CallExpression")) {
|
|
25721
26486
|
for (const callArgument of expression.arguments ?? []) {
|
|
25722
26487
|
if (isNodeOfType(callArgument, "ArrowFunctionExpression") || isNodeOfType(callArgument, "FunctionExpression")) {
|
|
@@ -25762,7 +26527,7 @@ const collectCalledSameFileHelperBodies = (handlerBody, resolveBinding) => {
|
|
|
25762
26527
|
if (visitedHelperNames.has(helperName)) return;
|
|
25763
26528
|
visitedHelperNames.add(helperName);
|
|
25764
26529
|
const helperBinding = resolveBinding(helperName);
|
|
25765
|
-
if (!isFunctionLike$
|
|
26530
|
+
if (!isFunctionLike$1(helperBinding) || !helperBinding.body) return;
|
|
25766
26531
|
if (helperBinding.body === handlerBody) return;
|
|
25767
26532
|
helperBodies.push(helperBinding.body);
|
|
25768
26533
|
});
|
|
@@ -26166,7 +26931,7 @@ const HOOK_NAME_PATTERN$2 = /^use[A-Z0-9]/;
|
|
|
26166
26931
|
const isInsideCallbackArgumentOf = (identifier, initializer) => {
|
|
26167
26932
|
if (!isNodeOfType(initializer, "CallExpression") && !isNodeOfType(initializer, "NewExpression")) return false;
|
|
26168
26933
|
if (isNodeOfType(initializer, "CallExpression") && isNodeOfType(initializer.callee, "Identifier") && HOOK_NAME_PATTERN$2.test(initializer.callee.name)) return false;
|
|
26169
|
-
const callbackArguments = (initializer.arguments ?? []).filter((argument) => isFunctionLike$
|
|
26934
|
+
const callbackArguments = (initializer.arguments ?? []).filter((argument) => isFunctionLike$1(argument));
|
|
26170
26935
|
if (callbackArguments.length === 0) return false;
|
|
26171
26936
|
let node = identifier;
|
|
26172
26937
|
while (node && node !== initializer) {
|
|
@@ -26302,10 +27067,10 @@ const resolveToFunction = (ref) => {
|
|
|
26302
27067
|
if (!definition || hasParameterDefinition(ref)) return null;
|
|
26303
27068
|
const definitionNode = definition.node;
|
|
26304
27069
|
if (!definitionNode) return null;
|
|
26305
|
-
if (isFunctionLike$
|
|
27070
|
+
if (isFunctionLike$1(definitionNode)) return definitionNode;
|
|
26306
27071
|
if (isNodeOfType(definitionNode, "VariableDeclarator")) {
|
|
26307
27072
|
const initializer = unwrapUseCallback(definitionNode.init);
|
|
26308
|
-
if (isFunctionLike$
|
|
27073
|
+
if (isFunctionLike$1(initializer)) return initializer;
|
|
26309
27074
|
}
|
|
26310
27075
|
return null;
|
|
26311
27076
|
};
|
|
@@ -26397,7 +27162,7 @@ const isDeferredCallbackFunction = (analysis, fn) => {
|
|
|
26397
27162
|
const isInsideDeferredCallback$1 = (analysis, node, boundary) => {
|
|
26398
27163
|
let current = parentOf(node);
|
|
26399
27164
|
while (current && current !== boundary) {
|
|
26400
|
-
if (isFunctionLike$
|
|
27165
|
+
if (isFunctionLike$1(current) && isDeferredCallbackFunction(analysis, current)) return true;
|
|
26401
27166
|
current = parentOf(current);
|
|
26402
27167
|
}
|
|
26403
27168
|
return false;
|
|
@@ -26637,9 +27402,9 @@ const isProp = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
|
|
|
26637
27402
|
return isReactFunctionalComponent(declaringNode) && !isReactFunctionalHOC(analysis, declaringNode) || isCustomHook(declaringNode);
|
|
26638
27403
|
}));
|
|
26639
27404
|
const isWholePropsParameterBinding = (bindingNode) => {
|
|
26640
|
-
if (isFunctionLike$
|
|
27405
|
+
if (isFunctionLike$1(bindingNode.parent)) return true;
|
|
26641
27406
|
let declaringFunction = bindingNode.parent;
|
|
26642
|
-
while (declaringFunction && !isFunctionLike$
|
|
27407
|
+
while (declaringFunction && !isFunctionLike$1(declaringFunction)) declaringFunction = declaringFunction.parent;
|
|
26643
27408
|
return Boolean(declaringFunction && resolveFirstArgumentBinding(declaringFunction.params?.[0]) === bindingNode);
|
|
26644
27409
|
};
|
|
26645
27410
|
const isWholePropsObjectReference = (analysis, ref) => isProp(analysis, ref) && Boolean(ref.resolved?.defs.some((def) => {
|
|
@@ -26706,7 +27471,7 @@ const getUseStateDecl = (analysis, ref) => {
|
|
|
26706
27471
|
return node ?? null;
|
|
26707
27472
|
};
|
|
26708
27473
|
const isCleanupReturnArgument = (analysis, node) => {
|
|
26709
|
-
if (isFunctionLike$
|
|
27474
|
+
if (isFunctionLike$1(node)) return true;
|
|
26710
27475
|
if (isNodeOfType(node, "MemberExpression")) return true;
|
|
26711
27476
|
if (isNodeOfType(node, "Identifier")) {
|
|
26712
27477
|
const ref = getRef(analysis, node);
|
|
@@ -26719,7 +27484,7 @@ const hasCleanupReturn = (analysis, node, visited = /* @__PURE__ */ new WeakSet(
|
|
|
26719
27484
|
if (visited.has(node)) return false;
|
|
26720
27485
|
visited.add(node);
|
|
26721
27486
|
if (isNodeOfType(node, "ReturnStatement") && node.argument != null) return isCleanupReturnArgument(analysis, node.argument);
|
|
26722
|
-
if (!isNodeOfType(node, "BlockStatement") && isFunctionLike$
|
|
27487
|
+
if (!isNodeOfType(node, "BlockStatement") && isFunctionLike$1(node)) return false;
|
|
26723
27488
|
const record = node;
|
|
26724
27489
|
const childKeys = getAstChildKeys(node);
|
|
26725
27490
|
for (let keyIndex = 0; keyIndex < childKeys.length; keyIndex += 1) {
|
|
@@ -26734,7 +27499,7 @@ const hasCleanupReturn = (analysis, node, visited = /* @__PURE__ */ new WeakSet(
|
|
|
26734
27499
|
};
|
|
26735
27500
|
const hasCleanup = (analysis, node) => {
|
|
26736
27501
|
const fn = getEffectFn(analysis, node);
|
|
26737
|
-
if (!isFunctionLike$
|
|
27502
|
+
if (!isFunctionLike$1(fn)) return false;
|
|
26738
27503
|
if (!isNodeOfType(fn.body, "BlockStatement")) return isCleanupReturnArgument(analysis, fn.body);
|
|
26739
27504
|
return hasCleanupReturn(analysis, fn.body);
|
|
26740
27505
|
};
|
|
@@ -26774,9 +27539,9 @@ const getOwnScopeBoundNames = (functionNode) => {
|
|
|
26774
27539
|
const cached = ownScopeBoundNamesCache.get(functionNode);
|
|
26775
27540
|
if (cached) return cached;
|
|
26776
27541
|
const boundNames = /* @__PURE__ */ new Set();
|
|
26777
|
-
if (isFunctionLike$
|
|
27542
|
+
if (isFunctionLike$1(functionNode)) for (const param of functionNode.params ?? []) collectPatternNames(param, boundNames);
|
|
26778
27543
|
walkAst(functionNode, (child) => {
|
|
26779
|
-
if (child !== functionNode && isFunctionLike$
|
|
27544
|
+
if (child !== functionNode && isFunctionLike$1(child)) return false;
|
|
26780
27545
|
if (isNodeOfType(child, "VariableDeclarator")) collectPatternNames(child.id, boundNames);
|
|
26781
27546
|
});
|
|
26782
27547
|
ownScopeBoundNamesCache.set(functionNode, boundNames);
|
|
@@ -26794,7 +27559,7 @@ const referencesIdentifierNamed = (root, identifierName) => {
|
|
|
26794
27559
|
let isReferenced = false;
|
|
26795
27560
|
walkAst(root, (child) => {
|
|
26796
27561
|
if (isReferenced) return false;
|
|
26797
|
-
if (child !== root && isFunctionLike$
|
|
27562
|
+
if (child !== root && isFunctionLike$1(child) && declaresBindingNamed(child, identifierName)) return false;
|
|
26798
27563
|
if (isNodeOfType(child, "Identifier") && child.name === identifierName && !isPropertyNamePosition(child)) {
|
|
26799
27564
|
isReferenced = true;
|
|
26800
27565
|
return false;
|
|
@@ -26806,7 +27571,7 @@ const isSetterWiredToJsxHandler = (componentFunction, setterName) => {
|
|
|
26806
27571
|
let isWired = false;
|
|
26807
27572
|
walkAst(componentFunction, (child) => {
|
|
26808
27573
|
if (isWired) return false;
|
|
26809
|
-
if (child !== componentFunction && isFunctionLike$
|
|
27574
|
+
if (child !== componentFunction && isFunctionLike$1(child) && declaresBindingNamed(child, setterName)) return false;
|
|
26810
27575
|
if (!isNodeOfType(child, "JSXAttribute") || !child.value) return;
|
|
26811
27576
|
const attributeName = getJsxAttributeName(child.name);
|
|
26812
27577
|
if (!attributeName || !isEventHandlerName(attributeName)) return;
|
|
@@ -26853,7 +27618,7 @@ const isIndependentWriterIdentifier = (componentFunction, identifier, includeDef
|
|
|
26853
27618
|
}
|
|
26854
27619
|
if (isEventHandlerPropertyKey(cursor)) return true;
|
|
26855
27620
|
if (includeDeferredWriters && isDeferredCallbackArgumentOf(cursor, previous)) return true;
|
|
26856
|
-
if (isFunctionLike$
|
|
27621
|
+
if (isFunctionLike$1(cursor)) {
|
|
26857
27622
|
outermostFunctionBelowComponent = cursor;
|
|
26858
27623
|
if (includeDeferredWriters && cursor.async === true) return true;
|
|
26859
27624
|
}
|
|
@@ -27048,7 +27813,7 @@ const getCallCalleeName$1 = (callExpression) => {
|
|
|
27048
27813
|
if (isNodeOfType(callee, "Identifier")) return callee.name;
|
|
27049
27814
|
return getStaticMemberName$1(callee);
|
|
27050
27815
|
};
|
|
27051
|
-
const getFunctionParameters = (functionNode) => isFunctionLike$
|
|
27816
|
+
const getFunctionParameters = (functionNode) => isFunctionLike$1(functionNode) ? functionNode.params ?? [] : [];
|
|
27052
27817
|
const getIdentifierBindingIdentity = (analysis, identifier) => {
|
|
27053
27818
|
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
27054
27819
|
return getRef(analysis, identifier)?.resolved ?? null;
|
|
@@ -27065,7 +27830,7 @@ const isAsyncOrGeneratorFunction = (functionNode) => Boolean(functionNode.async
|
|
|
27065
27830
|
const isModuleFunction = (functionNode) => {
|
|
27066
27831
|
let ancestor = functionNode.parent;
|
|
27067
27832
|
while (ancestor) {
|
|
27068
|
-
if (isFunctionLike$
|
|
27833
|
+
if (isFunctionLike$1(ancestor)) return false;
|
|
27069
27834
|
if (isNodeOfType(ancestor, "Program")) return true;
|
|
27070
27835
|
ancestor = ancestor.parent;
|
|
27071
27836
|
}
|
|
@@ -27138,7 +27903,7 @@ const localUseEventPreservesCallback = (analysis, callee) => {
|
|
|
27138
27903
|
const callbackBinding = getParameterBindingIdentity(analysis, implementation, callbackParameter);
|
|
27139
27904
|
const callbackRefDeclarators = [];
|
|
27140
27905
|
walkAst(implementation, (child) => {
|
|
27141
|
-
if (child !== implementation && isFunctionLike$
|
|
27906
|
+
if (child !== implementation && isFunctionLike$1(child)) return false;
|
|
27142
27907
|
if (!isNodeOfType(child, "VariableDeclarator")) return;
|
|
27143
27908
|
if (!isNodeOfType(child.id, "Identifier")) return;
|
|
27144
27909
|
if (!isNodeOfType(child.init, "CallExpression")) return;
|
|
@@ -27154,11 +27919,11 @@ const localUseEventPreservesCallback = (analysis, callee) => {
|
|
|
27154
27919
|
const returnedCalleeName = getCallCalleeName$1(returnedCall);
|
|
27155
27920
|
if (returnedCalleeName !== "useCallback" && returnedCalleeName !== "useEffectEvent") return false;
|
|
27156
27921
|
const stableCallback = returnedCall.arguments?.[0];
|
|
27157
|
-
if (!stableCallback || !isFunctionLike$
|
|
27922
|
+
if (!stableCallback || !isFunctionLike$1(stableCallback)) return false;
|
|
27158
27923
|
let forwardsCallback = false;
|
|
27159
27924
|
walkAst(stableCallback, (child) => {
|
|
27160
27925
|
if (forwardsCallback) return false;
|
|
27161
|
-
if (child !== stableCallback && isFunctionLike$
|
|
27926
|
+
if (child !== stableCallback && isFunctionLike$1(child)) return false;
|
|
27162
27927
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
27163
27928
|
const forwardedCallee = stripParenExpression(child.callee);
|
|
27164
27929
|
if (!isNodeOfType(forwardedCallee, "MemberExpression")) return;
|
|
@@ -27178,7 +27943,7 @@ const localUseEventPreservesCallback = (analysis, callee) => {
|
|
|
27178
27943
|
};
|
|
27179
27944
|
const resolveWrappedCallable = (analysis, node) => {
|
|
27180
27945
|
const candidate = stripParenExpression(node);
|
|
27181
|
-
if (isFunctionLike$
|
|
27946
|
+
if (isFunctionLike$1(candidate)) return candidate;
|
|
27182
27947
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
27183
27948
|
const reference = getRef(analysis, candidate);
|
|
27184
27949
|
if (!reference) return null;
|
|
@@ -27191,7 +27956,7 @@ const resolveWrappedCallable = (analysis, node) => {
|
|
|
27191
27956
|
if (!initializer || !isNodeOfType(initializer, "CallExpression")) return null;
|
|
27192
27957
|
if (!isReactUseEffectEventCallee(analysis, initializer.callee) && !localUseEventPreservesCallback(analysis, initializer.callee)) return null;
|
|
27193
27958
|
const callback = initializer.arguments?.[0];
|
|
27194
|
-
return callback && isFunctionLike$
|
|
27959
|
+
return callback && isFunctionLike$1(callback) ? callback : null;
|
|
27195
27960
|
}
|
|
27196
27961
|
if (!isNodeOfType(candidate, "MemberExpression")) return null;
|
|
27197
27962
|
if (getStaticMemberName$1(candidate) !== "current") return null;
|
|
@@ -27202,7 +27967,7 @@ const resolveWrappedCallable = (analysis, node) => {
|
|
|
27202
27967
|
if (!isNodeOfType(declarator.init, "CallExpression")) return null;
|
|
27203
27968
|
if (getCallCalleeName$1(declarator.init) !== "useRef") return null;
|
|
27204
27969
|
const initializer = declarator.init.arguments?.[0];
|
|
27205
|
-
if (!initializer || !isFunctionLike$
|
|
27970
|
+
if (!initializer || !isFunctionLike$1(initializer)) return null;
|
|
27206
27971
|
return Boolean(reference?.resolved?.references.some((candidateReference) => {
|
|
27207
27972
|
const member = candidateReference.identifier.parent;
|
|
27208
27973
|
const assignment = member?.parent;
|
|
@@ -27213,7 +27978,7 @@ const functionInvokesItself = (analysis, functionNode) => {
|
|
|
27213
27978
|
let invokesItself = false;
|
|
27214
27979
|
walkAst(functionNode, (child) => {
|
|
27215
27980
|
if (invokesItself) return false;
|
|
27216
|
-
if (child !== functionNode && isFunctionLike$
|
|
27981
|
+
if (child !== functionNode && isFunctionLike$1(child)) return false;
|
|
27217
27982
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
27218
27983
|
if (resolveWrappedCallable(analysis, child.callee) === functionNode) {
|
|
27219
27984
|
invokesItself = true;
|
|
@@ -27229,7 +27994,7 @@ const collectIntroducedBindings = (analysis, functionNode) => {
|
|
|
27229
27994
|
};
|
|
27230
27995
|
const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilename) => {
|
|
27231
27996
|
const effectFunction = getEffectFn(analysis, effectNode);
|
|
27232
|
-
if (!effectFunction || !isFunctionLike$
|
|
27997
|
+
if (!effectFunction || !isFunctionLike$1(effectFunction) || effectFunction.async === true) return [];
|
|
27233
27998
|
const invokedFunctionEvidence = collectEffectInvokedFunctions(effectFunction);
|
|
27234
27999
|
const rootFrame = {
|
|
27235
28000
|
functionNode: effectFunction,
|
|
@@ -27241,7 +28006,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
|
|
|
27241
28006
|
};
|
|
27242
28007
|
const frames = [rootFrame];
|
|
27243
28008
|
walkAst(effectFunction, (child) => {
|
|
27244
|
-
if (child !== effectFunction && isFunctionLike$
|
|
28009
|
+
if (child !== effectFunction && isFunctionLike$1(child)) return false;
|
|
27245
28010
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
27246
28011
|
const callee = stripParenExpression(child.callee);
|
|
27247
28012
|
const calleeName = getCallCalleeName$1(child);
|
|
@@ -27262,7 +28027,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
|
|
|
27262
28027
|
currentFilename
|
|
27263
28028
|
});
|
|
27264
28029
|
};
|
|
27265
|
-
if (isFunctionLike$
|
|
28030
|
+
if (isFunctionLike$1(callee)) {
|
|
27266
28031
|
enqueueFrame(callee, child.arguments ?? [], false, /* @__PURE__ */ new Set());
|
|
27267
28032
|
return;
|
|
27268
28033
|
}
|
|
@@ -27299,11 +28064,11 @@ const mergeEvidence = (target, source) => {
|
|
|
27299
28064
|
target.readsExternalValue ||= source.readsExternalValue;
|
|
27300
28065
|
};
|
|
27301
28066
|
const getReturnedExpressions = (functionNode) => {
|
|
27302
|
-
if (!isFunctionLike$
|
|
28067
|
+
if (!isFunctionLike$1(functionNode)) return [];
|
|
27303
28068
|
if (!isNodeOfType(functionNode.body, "BlockStatement")) return [functionNode.body];
|
|
27304
28069
|
const returnedExpressions = [];
|
|
27305
28070
|
walkAst(functionNode.body, (child) => {
|
|
27306
|
-
if (child !== functionNode.body && isFunctionLike$
|
|
28071
|
+
if (child !== functionNode.body && isFunctionLike$1(child)) return false;
|
|
27307
28072
|
if (isNodeOfType(child, "ReturnStatement") && child.argument) returnedExpressions.push(child.argument);
|
|
27308
28073
|
});
|
|
27309
28074
|
return returnedExpressions;
|
|
@@ -27388,7 +28153,7 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
|
|
|
27388
28153
|
if (!analyzeHelperExpression(node.object, environment, usedParameterIndices)) return false;
|
|
27389
28154
|
return !node.computed || analyzeHelperExpression(node.property, environment, usedParameterIndices);
|
|
27390
28155
|
}
|
|
27391
|
-
if (isFunctionLike$
|
|
28156
|
+
if (isFunctionLike$1(node)) {
|
|
27392
28157
|
if (isAsyncOrGeneratorFunction(node)) return false;
|
|
27393
28158
|
const callbackParameterIndices = new Map(environment.parameterIndices);
|
|
27394
28159
|
for (const parameter of getFunctionParameters(node)) {
|
|
@@ -27426,7 +28191,7 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
|
|
|
27426
28191
|
};
|
|
27427
28192
|
const helperSummaryCache = /* @__PURE__ */ new WeakMap();
|
|
27428
28193
|
const summarizeHelperReturn = (functionNode) => {
|
|
27429
|
-
if (!isFunctionLike$
|
|
28194
|
+
if (!isFunctionLike$1(functionNode) || !isModuleFunction(functionNode)) return null;
|
|
27430
28195
|
if (helperSummaryCache.has(functionNode)) return helperSummaryCache.get(functionNode) ?? null;
|
|
27431
28196
|
if (isAsyncOrGeneratorFunction(functionNode)) {
|
|
27432
28197
|
helperSummaryCache.set(functionNode, null);
|
|
@@ -27616,7 +28381,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
27616
28381
|
if (isPureGlobalCall || isPureMemberTransform) {
|
|
27617
28382
|
if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
27618
28383
|
for (const argument of node.arguments ?? []) {
|
|
27619
|
-
if (isFunctionLike$
|
|
28384
|
+
if (isFunctionLike$1(argument)) continue;
|
|
27620
28385
|
mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
27621
28386
|
}
|
|
27622
28387
|
return evidence;
|
|
@@ -27673,7 +28438,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
27673
28438
|
for (const argument of node.arguments ?? []) mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
27674
28439
|
return evidence;
|
|
27675
28440
|
}
|
|
27676
|
-
if (isFunctionLike$
|
|
28441
|
+
if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
|
|
27677
28442
|
evidence.hasUnknownSource = true;
|
|
27678
28443
|
return evidence;
|
|
27679
28444
|
}
|
|
@@ -27740,7 +28505,7 @@ const matchesStateInitializer = (analysis, callExpression, stateDeclarator) => {
|
|
|
27740
28505
|
const collectFrameSetterCalls = (analysis, frame) => {
|
|
27741
28506
|
const calls = [];
|
|
27742
28507
|
walkAst(frame.functionNode, (child) => {
|
|
27743
|
-
if (child !== frame.functionNode && isFunctionLike$
|
|
28508
|
+
if (child !== frame.functionNode && isFunctionLike$1(child)) return false;
|
|
27744
28509
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
27745
28510
|
const setterReference = findStateSetterReference(analysis, child);
|
|
27746
28511
|
if (setterReference) calls.push({
|
|
@@ -27779,7 +28544,7 @@ const collectEffectStateWriteFacts = (analysis, effectNode, currentFilename) =>
|
|
|
27779
28544
|
if (!stateDeclarator) continue;
|
|
27780
28545
|
const remainingValueCallFrames = frame === frames[0] ? 1 : 0;
|
|
27781
28546
|
let valueEvidence;
|
|
27782
|
-
if (isFunctionLike$
|
|
28547
|
+
if (isFunctionLike$1(writtenValue)) {
|
|
27783
28548
|
const updaterFrame = {
|
|
27784
28549
|
functionNode: writtenValue,
|
|
27785
28550
|
invocation: callExpression,
|
|
@@ -28346,13 +29111,13 @@ const hasEmptyDependencyArray = (useMemoCall) => {
|
|
|
28346
29111
|
const useMemoReturnsArrayLiteral = (useMemoCall) => {
|
|
28347
29112
|
if (!isNodeOfType(useMemoCall, "CallExpression")) return false;
|
|
28348
29113
|
const factory = useMemoCall.arguments?.[0];
|
|
28349
|
-
if (!factory || !isFunctionLike$
|
|
29114
|
+
if (!factory || !isFunctionLike$1(factory)) return false;
|
|
28350
29115
|
const body = factory.body;
|
|
28351
29116
|
if (!isNodeOfType(body, "BlockStatement")) return isSpreadFreeArrayLiteral(body);
|
|
28352
29117
|
let didFindReturn = false;
|
|
28353
29118
|
let allReturnsAreFixedArrays = true;
|
|
28354
29119
|
walkAst(body, (child) => {
|
|
28355
|
-
if (isFunctionLike$
|
|
29120
|
+
if (isFunctionLike$1(child)) return false;
|
|
28356
29121
|
if (isNodeOfType(child, "ReturnStatement")) {
|
|
28357
29122
|
didFindReturn = true;
|
|
28358
29123
|
if (!child.argument || !isSpreadFreeArrayLiteral(child.argument)) allReturnsAreFixedArrays = false;
|
|
@@ -28495,7 +29260,7 @@ const findEnclosingParameter = (bindingIdentifier) => {
|
|
|
28495
29260
|
let current = bindingIdentifier;
|
|
28496
29261
|
while (current.parent) {
|
|
28497
29262
|
const parent = current.parent;
|
|
28498
|
-
if (isFunctionLike$
|
|
29263
|
+
if (isFunctionLike$1(parent)) {
|
|
28499
29264
|
const parameterPosition = (parent.params ?? []).indexOf(current);
|
|
28500
29265
|
return parameterPosition >= 0 ? {
|
|
28501
29266
|
functionNode: parent,
|
|
@@ -28731,7 +29496,7 @@ const isNumericForLoopCounter = (attributeNode, indexName) => {
|
|
|
28731
29496
|
const EMPTY_NAME_SET = /* @__PURE__ */ new Set();
|
|
28732
29497
|
const findIteratorItemNamesOfBinding = (binding) => {
|
|
28733
29498
|
const names = /* @__PURE__ */ new Set();
|
|
28734
|
-
if (!binding.bindingFunction || !isFunctionLike$
|
|
29499
|
+
if (!binding.bindingFunction || !isFunctionLike$1(binding.bindingFunction)) return names;
|
|
28735
29500
|
if (binding.indexParameterPosition === null || binding.indexParameterPosition < 1) return names;
|
|
28736
29501
|
const firstParameter = (binding.bindingFunction.params ?? [])[0];
|
|
28737
29502
|
if (firstParameter) collectPatternNames(firstParameter, names);
|
|
@@ -28745,7 +29510,7 @@ const collectDerivedRowContentNames = (bindingFunction, itemNames) => {
|
|
|
28745
29510
|
walkAst(bindingFunction, (child) => {
|
|
28746
29511
|
if (budget <= 0) return false;
|
|
28747
29512
|
budget -= 1;
|
|
28748
|
-
if (isFunctionLike$
|
|
29513
|
+
if (isFunctionLike$1(child) && child !== bindingFunction) return false;
|
|
28749
29514
|
if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && child.init) {
|
|
28750
29515
|
const rootName = getRootIdentifierName(child.init, { followCallChains: true });
|
|
28751
29516
|
if (rootName !== null && itemNames.has(rootName)) names.add(child.id.name);
|
|
@@ -28777,7 +29542,7 @@ const callbackFiltersRows = (bindingFunction) => {
|
|
|
28777
29542
|
let didFindNullReturn = false;
|
|
28778
29543
|
walkAst(bindingFunction, (child) => {
|
|
28779
29544
|
if (didFindNullReturn) return false;
|
|
28780
|
-
if (isFunctionLike$
|
|
29545
|
+
if (isFunctionLike$1(child) && child !== bindingFunction) return false;
|
|
28781
29546
|
if (isNodeOfType(child, "ReturnStatement") && child.argument && isNodeOfType(child.argument, "Literal") && child.argument.value === null) {
|
|
28782
29547
|
didFindNullReturn = true;
|
|
28783
29548
|
return false;
|
|
@@ -28800,7 +29565,7 @@ const hasAriaHiddenAncestor = (attributeNode) => {
|
|
|
28800
29565
|
let current = attributeNode.parent;
|
|
28801
29566
|
while (current) {
|
|
28802
29567
|
if (isNodeOfType(current, "Program")) return false;
|
|
28803
|
-
if (isFunctionLike$
|
|
29568
|
+
if (isFunctionLike$1(current)) {
|
|
28804
29569
|
const parent = current.parent;
|
|
28805
29570
|
if (!(isNodeOfType(parent, "CallExpression") && parent.arguments.includes(current))) return false;
|
|
28806
29571
|
}
|
|
@@ -29766,7 +30531,7 @@ const symbolIsLocalComponent = (symbol, context) => {
|
|
|
29766
30531
|
const isModuleScopeDeclaration = (declaration) => {
|
|
29767
30532
|
let current = declaration?.parent;
|
|
29768
30533
|
while (current) {
|
|
29769
|
-
if (isFunctionLike$
|
|
30534
|
+
if (isFunctionLike$1(current)) return false;
|
|
29770
30535
|
current = current.parent ?? null;
|
|
29771
30536
|
}
|
|
29772
30537
|
return true;
|
|
@@ -29788,12 +30553,12 @@ const declarationBodyContainsHookCall = (symbol) => {
|
|
|
29788
30553
|
};
|
|
29789
30554
|
const declarationTakesMultiplePositionalArguments = (symbol) => {
|
|
29790
30555
|
const componentFunction = isComponentDeclaration(symbol.declarationNode) ? symbol.declarationNode : symbol.initializer;
|
|
29791
|
-
if (!componentFunction || !isFunctionLike$
|
|
30556
|
+
if (!componentFunction || !isFunctionLike$1(componentFunction)) return false;
|
|
29792
30557
|
return (componentFunction.params?.length ?? 0) >= 2;
|
|
29793
30558
|
};
|
|
29794
30559
|
const declarationIsAsyncFunction = (symbol) => {
|
|
29795
30560
|
const componentFunction = isComponentDeclaration(symbol.declarationNode) ? symbol.declarationNode : symbol.initializer;
|
|
29796
|
-
if (!componentFunction || !isFunctionLike$
|
|
30561
|
+
if (!componentFunction || !isFunctionLike$1(componentFunction)) return false;
|
|
29797
30562
|
return componentFunction.async === true;
|
|
29798
30563
|
};
|
|
29799
30564
|
const isReturnedFromUseCallbackAdapter = (callNode) => {
|
|
@@ -29898,7 +30663,7 @@ const isSimpleExpression$1 = (analysis, expression, effectFn, visitedDeclarators
|
|
|
29898
30663
|
let isSimple = true;
|
|
29899
30664
|
walkAst(expression, (child) => {
|
|
29900
30665
|
if (!isSimple) return false;
|
|
29901
|
-
if (isFunctionLike$
|
|
30666
|
+
if (isFunctionLike$1(child) || isNodeOfType(child, "AwaitExpression") || isNodeOfType(child, "NewExpression")) {
|
|
29902
30667
|
isSimple = false;
|
|
29903
30668
|
return false;
|
|
29904
30669
|
}
|
|
@@ -30683,7 +31448,7 @@ const isFunctionAssignedToComponent = (functionNode) => {
|
|
|
30683
31448
|
return isNodeOfType(cursor, "ExportDefaultDeclaration");
|
|
30684
31449
|
};
|
|
30685
31450
|
const isComponentFunction$1 = (node) => {
|
|
30686
|
-
if (!isFunctionLike$
|
|
31451
|
+
if (!isFunctionLike$1(node)) return false;
|
|
30687
31452
|
if (isNodeOfType(node, "FunctionDeclaration")) return !node.id || node.id.name === "default" || isUppercaseName(node.id.name) || isNodeOfType(node.parent, "ExportDefaultDeclaration");
|
|
30688
31453
|
return isFunctionAssignedToComponent(node);
|
|
30689
31454
|
};
|
|
@@ -30696,7 +31461,7 @@ const extractDestructuredPropNames = (params) => {
|
|
|
30696
31461
|
};
|
|
30697
31462
|
const getInlineFunctionNode = (node) => {
|
|
30698
31463
|
if (!node) return null;
|
|
30699
|
-
if (isFunctionLike$
|
|
31464
|
+
if (isFunctionLike$1(node)) return node;
|
|
30700
31465
|
if (!isNodeOfType(node, "CallExpression")) return null;
|
|
30701
31466
|
for (const argument of node.arguments ?? []) {
|
|
30702
31467
|
const inlineFunctionNode = getInlineFunctionNode(argument);
|
|
@@ -30707,7 +31472,7 @@ const getInlineFunctionNode = (node) => {
|
|
|
30707
31472
|
const getNearestComponentFunction = (node) => {
|
|
30708
31473
|
let cursor = node.parent ?? null;
|
|
30709
31474
|
while (cursor) {
|
|
30710
|
-
if (isFunctionLike$
|
|
31475
|
+
if (isFunctionLike$1(cursor)) return cursor;
|
|
30711
31476
|
cursor = cursor.parent ?? null;
|
|
30712
31477
|
}
|
|
30713
31478
|
return null;
|
|
@@ -30949,7 +31714,7 @@ const getStateInitializer = (stateDeclarator) => {
|
|
|
30949
31714
|
return initializer ? initializer : null;
|
|
30950
31715
|
};
|
|
30951
31716
|
const collectRenderStateWriteFacts = (analysis, componentBody, currentFilename) => {
|
|
30952
|
-
if (!isNodeOfType(componentBody, "BlockStatement") || !componentBody.parent || !isFunctionLike$
|
|
31717
|
+
if (!isNodeOfType(componentBody, "BlockStatement") || !componentBody.parent || !isFunctionLike$1(componentBody.parent)) return [];
|
|
30953
31718
|
const componentFunction = componentBody.parent;
|
|
30954
31719
|
const facts = [];
|
|
30955
31720
|
for (const statement of componentBody.body ?? []) {
|
|
@@ -30964,7 +31729,7 @@ const collectRenderStateWriteFacts = (analysis, componentBody, currentFilename)
|
|
|
30964
31729
|
if (!stateDeclarator || !isGenuineReactHookDeclarator(analysis, stateDeclarator, "useState") || guard.tracker.kind === "state" && stateDeclarator === guard.tracker.declarator) continue;
|
|
30965
31730
|
const writtenValue = callExpression.arguments?.[0];
|
|
30966
31731
|
const initializer = getStateInitializer(stateDeclarator);
|
|
30967
|
-
if (!writtenValue || !initializer || isFunctionLike$
|
|
31732
|
+
if (!writtenValue || !initializer || isFunctionLike$1(writtenValue) || !doesEvidenceMatchSource(collectRenderValueEvidence(analysis, writtenValue, componentFunction, currentFilename), guard.source) || !doesEvidenceMatchSource(collectRenderValueEvidence(analysis, initializer, componentFunction, currentFilename), guard.source)) continue;
|
|
30968
31733
|
facts.push({
|
|
30969
31734
|
callExpression,
|
|
30970
31735
|
stateDeclarator
|
|
@@ -31104,7 +31869,7 @@ const isComponentScopeValueArgument = (setterCall, componentFunction, argument)
|
|
|
31104
31869
|
if (rootName === null) return false;
|
|
31105
31870
|
let cursor = setterCall.parent ?? null;
|
|
31106
31871
|
while (cursor && cursor !== componentFunction) {
|
|
31107
|
-
if (isFunctionLike$
|
|
31872
|
+
if (isFunctionLike$1(cursor)) {
|
|
31108
31873
|
const nestedParamNames = /* @__PURE__ */ new Set();
|
|
31109
31874
|
for (const param of cursor.params ?? []) collectPatternNames(param, nestedParamNames);
|
|
31110
31875
|
if (nestedParamNames.has(rootName)) return false;
|
|
@@ -31147,7 +31912,7 @@ const hasSessionDismissProp = (propNames) => {
|
|
|
31147
31912
|
const getEnclosingEffectHookCallback = (node, componentFunction) => {
|
|
31148
31913
|
let cursor = node.parent ?? null;
|
|
31149
31914
|
while (cursor && cursor !== componentFunction) {
|
|
31150
|
-
if (isFunctionLike$
|
|
31915
|
+
if (isFunctionLike$1(cursor)) {
|
|
31151
31916
|
const parent = cursor.parent ?? null;
|
|
31152
31917
|
if (parent && isNodeOfType(parent, "CallExpression")) {
|
|
31153
31918
|
const calleeName = getCalleeName$2(parent);
|
|
@@ -31228,7 +31993,7 @@ const isHandlerShapedReseed = (setterCall, componentFunction) => {
|
|
|
31228
31993
|
let hasNestedFunction = false;
|
|
31229
31994
|
let cursor = setterCall.parent ?? null;
|
|
31230
31995
|
while (cursor && cursor !== componentFunction) {
|
|
31231
|
-
if (isFunctionLike$
|
|
31996
|
+
if (isFunctionLike$1(cursor)) {
|
|
31232
31997
|
hasNestedFunction = true;
|
|
31233
31998
|
if (isNonHandlerHookCallback(cursor)) return false;
|
|
31234
31999
|
}
|
|
@@ -31239,7 +32004,7 @@ const isHandlerShapedReseed = (setterCall, componentFunction) => {
|
|
|
31239
32004
|
const isInRenderScope = (node, componentFunction) => {
|
|
31240
32005
|
let cursor = node.parent ?? null;
|
|
31241
32006
|
while (cursor && cursor !== componentFunction) {
|
|
31242
|
-
if (isFunctionLike$
|
|
32007
|
+
if (isFunctionLike$1(cursor)) return false;
|
|
31243
32008
|
cursor = cursor.parent ?? null;
|
|
31244
32009
|
}
|
|
31245
32010
|
return true;
|
|
@@ -31253,10 +32018,10 @@ const getStateValueName = (useStateCall) => {
|
|
|
31253
32018
|
return valueElement.name;
|
|
31254
32019
|
};
|
|
31255
32020
|
const isHookDrivenFunctionalUpdate = (setterCall, componentFunction, argument) => {
|
|
31256
|
-
if (!argument || !isFunctionLike$
|
|
32021
|
+
if (!argument || !isFunctionLike$1(argument)) return false;
|
|
31257
32022
|
let cursor = setterCall.parent ?? null;
|
|
31258
32023
|
while (cursor && cursor !== componentFunction) {
|
|
31259
|
-
if (isFunctionLike$
|
|
32024
|
+
if (isFunctionLike$1(cursor) && isNonHandlerHookCallback(cursor)) return true;
|
|
31260
32025
|
cursor = cursor.parent ?? null;
|
|
31261
32026
|
}
|
|
31262
32027
|
return false;
|
|
@@ -31378,7 +32143,7 @@ const getNodeStart = (node) => "start" in node && typeof node.start === "number"
|
|
|
31378
32143
|
const getEnclosingLifecycleFunction = (setStateCall) => {
|
|
31379
32144
|
let ancestor = setStateCall.parent;
|
|
31380
32145
|
while (ancestor) {
|
|
31381
|
-
if (isFunctionLike$
|
|
32146
|
+
if (isFunctionLike$1(ancestor)) {
|
|
31382
32147
|
const parent = ancestor.parent;
|
|
31383
32148
|
if ((isNodeOfType(parent, "MethodDefinition") || isNodeOfType(parent, "PropertyDefinition") || isNodeOfType(parent, "Property")) && isNodeOfType(parent.key, "Identifier") && LIFECYCLE_NAMES$2.has(parent.key.name)) return ancestor;
|
|
31384
32149
|
}
|
|
@@ -31462,7 +32227,7 @@ const argumentDerivesFromPostMountSource = (setStateCall, lifecycleFunction) =>
|
|
|
31462
32227
|
return false;
|
|
31463
32228
|
};
|
|
31464
32229
|
const isAfterAwaitInAsyncLifecycle = (setStateCall, lifecycleFunction) => {
|
|
31465
|
-
if (!isFunctionLike$
|
|
32230
|
+
if (!isFunctionLike$1(lifecycleFunction) || lifecycleFunction.async !== true) return false;
|
|
31466
32231
|
const callStart = getNodeStart(setStateCall);
|
|
31467
32232
|
if (callStart < 0) return false;
|
|
31468
32233
|
let didFindPrecedingAwait = false;
|
|
@@ -31906,7 +32671,7 @@ const collectBlockScopedBindings = (node) => {
|
|
|
31906
32671
|
const walkComponentRespectingShadows = (node, shadowedStateNames, visit, isComponentBodyRoot = false) => {
|
|
31907
32672
|
if (!node || typeof node !== "object") return;
|
|
31908
32673
|
let nextShadowedStateNames = shadowedStateNames;
|
|
31909
|
-
const localBindings = isComponentBodyRoot ? null : isFunctionLike$
|
|
32674
|
+
const localBindings = isComponentBodyRoot ? null : isFunctionLike$1(node) ? collectFunctionLocalBindings(node) : collectBlockScopedBindings(node);
|
|
31910
32675
|
if (localBindings && localBindings.size > 0) {
|
|
31911
32676
|
const merged = new Set(shadowedStateNames);
|
|
31912
32677
|
for (const localName of localBindings) merged.add(localName);
|
|
@@ -32443,7 +33208,7 @@ const containsReleaseLikeCall = (node, knownCleanupFunctionNames, knownBoundSubs
|
|
|
32443
33208
|
let didFindRelease = false;
|
|
32444
33209
|
walkAst(node, (child) => {
|
|
32445
33210
|
if (didFindRelease) return false;
|
|
32446
|
-
if (child !== node && isFunctionLike$
|
|
33211
|
+
if (child !== node && isFunctionLike$1(child) && !isIteratorCallbackArgument(child)) return false;
|
|
32447
33212
|
if (isReleaseLikeCall(child, knownCleanupFunctionNames, knownBoundSubscriptionNames)) {
|
|
32448
33213
|
didFindRelease = true;
|
|
32449
33214
|
return false;
|
|
@@ -32452,16 +33217,16 @@ const containsReleaseLikeCall = (node, knownCleanupFunctionNames, knownBoundSubs
|
|
|
32452
33217
|
return didFindRelease;
|
|
32453
33218
|
};
|
|
32454
33219
|
const isCleanupFunctionLike = (node, knownCleanupFunctionNames, knownBoundSubscriptionNames) => {
|
|
32455
|
-
if (!isFunctionLike$
|
|
33220
|
+
if (!isFunctionLike$1(node)) return false;
|
|
32456
33221
|
return containsReleaseLikeCall(node.body, knownCleanupFunctionNames, knownBoundSubscriptionNames);
|
|
32457
33222
|
};
|
|
32458
33223
|
const isProvablyNoOpCleanupFunction = (node) => {
|
|
32459
|
-
if (!isFunctionLike$
|
|
33224
|
+
if (!isFunctionLike$1(node)) return false;
|
|
32460
33225
|
let sawNoOpRemoval = false;
|
|
32461
33226
|
let sawOtherCall = false;
|
|
32462
33227
|
walkAst(node.body, (child) => {
|
|
32463
33228
|
if (sawOtherCall) return false;
|
|
32464
|
-
if (isFunctionLike$
|
|
33229
|
+
if (isFunctionLike$1(child)) return false;
|
|
32465
33230
|
const callNode = unwrapChainExpression$2(child);
|
|
32466
33231
|
if (!isNodeOfType(callNode, "CallExpression")) return;
|
|
32467
33232
|
const callee = unwrapChainExpression$2(callNode.callee);
|
|
@@ -32515,12 +33280,12 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
|
|
|
32515
33280
|
const pendingFunctions = [effectCallback];
|
|
32516
33281
|
while (pendingFunctions.length > 0) {
|
|
32517
33282
|
const currentFunction = pendingFunctions.pop();
|
|
32518
|
-
if (!currentFunction || !isFunctionLike$
|
|
33283
|
+
if (!currentFunction || !isFunctionLike$1(currentFunction)) continue;
|
|
32519
33284
|
walkInsideStatementBlocks(currentFunction.body, (child) => {
|
|
32520
33285
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
32521
33286
|
const invokedFunction = resolveExactLocalFunction(child.callee, scopes);
|
|
32522
33287
|
if (!invokedFunction || analysisFunctions.has(invokedFunction)) return;
|
|
32523
|
-
if (isFunctionLike$
|
|
33288
|
+
if (isFunctionLike$1(invokedFunction) && invokedFunction.async) return;
|
|
32524
33289
|
analysisFunctions.add(invokedFunction);
|
|
32525
33290
|
pendingFunctions.push(invokedFunction);
|
|
32526
33291
|
});
|
|
@@ -32529,7 +33294,7 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
|
|
|
32529
33294
|
};
|
|
32530
33295
|
const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
|
|
32531
33296
|
for (const analysisFunction of analysisFunctions) {
|
|
32532
|
-
if (!isFunctionLike$
|
|
33297
|
+
if (!isFunctionLike$1(analysisFunction)) continue;
|
|
32533
33298
|
walkInsideStatementBlocks(analysisFunction.body, visitor);
|
|
32534
33299
|
}
|
|
32535
33300
|
};
|
|
@@ -32608,7 +33373,7 @@ const isExternalSyncNode = (node) => {
|
|
|
32608
33373
|
return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
|
|
32609
33374
|
};
|
|
32610
33375
|
const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName) => {
|
|
32611
|
-
if (!isFunctionLike$
|
|
33376
|
+
if (!isFunctionLike$1(effectCallback)) return false;
|
|
32612
33377
|
if (!isNodeOfType(effectCallback.body, "BlockStatement")) {
|
|
32613
33378
|
if (isFunctionShapedReturn(effectCallback.body, setterToStateName, false)) return true;
|
|
32614
33379
|
} else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, true)) return true;
|
|
@@ -32635,7 +33400,7 @@ const noEffectChain = defineRule({
|
|
|
32635
33400
|
const effectInfos = [];
|
|
32636
33401
|
for (const effectCall of findTopLevelEffectCalls(componentBody)) {
|
|
32637
33402
|
const callback = getEffectCallback(effectCall, context.scopes);
|
|
32638
|
-
if (!callback || !isFunctionLike$
|
|
33403
|
+
if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
|
|
32639
33404
|
const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
|
|
32640
33405
|
const writtenStateNames = collectWrittenStateNamesInEffect(analysisFunctions, setterToStateName);
|
|
32641
33406
|
effectInfos.push({
|
|
@@ -33111,6 +33876,10 @@ const noEffectWithFreshDeps = defineRule({
|
|
|
33111
33876
|
category: "State & Effects",
|
|
33112
33877
|
recommendation: "Move the value inside the hook body and depend on its simple inputs instead, or wrap it in useMemo / useCallback so it stays the same between renders.",
|
|
33113
33878
|
create: (context) => ({ CallExpression(node) {
|
|
33879
|
+
for (const finding of findForwardedFreshHookDependencies(node, context, EFFECT_HOOK_NAMES$1)) context.report({
|
|
33880
|
+
node: finding.reportNode,
|
|
33881
|
+
message: `A dependency inside this custom Hook changes every render because \`${finding.bindingName}\` is a new ${finding.kind} built fresh each time.`
|
|
33882
|
+
});
|
|
33114
33883
|
if (!isHookCall$2(node, HOOKS_WITH_DEPS)) return;
|
|
33115
33884
|
const args = node.arguments ?? [];
|
|
33116
33885
|
if (args.length < 2) return;
|
|
@@ -33199,7 +33968,7 @@ const findFunctionDefinitionNode = (functionNode) => {
|
|
|
33199
33968
|
let current = functionNode;
|
|
33200
33969
|
while (current) {
|
|
33201
33970
|
if (isNodeOfType(current, "VariableDeclarator")) return current;
|
|
33202
|
-
if (isNodeOfType(current, "Program") || isFunctionLike$
|
|
33971
|
+
if (isNodeOfType(current, "Program") || isFunctionLike$1(current) && current !== functionNode) return null;
|
|
33203
33972
|
current = current.parent;
|
|
33204
33973
|
}
|
|
33205
33974
|
return null;
|
|
@@ -33219,7 +33988,7 @@ const isFunctionWiredToEventAttribute = (analysis, functionNode) => {
|
|
|
33219
33988
|
if (!variable) return false;
|
|
33220
33989
|
return variable.references.some((reference) => {
|
|
33221
33990
|
let current = reference.identifier;
|
|
33222
|
-
while (current && !isFunctionLike$
|
|
33991
|
+
while (current && !isFunctionLike$1(current)) {
|
|
33223
33992
|
if (isEventAttribute(current) || isEventProperty(current)) return true;
|
|
33224
33993
|
current = current.parent;
|
|
33225
33994
|
}
|
|
@@ -33262,7 +34031,7 @@ const isInsideProvenEventHandler = (analysis, node, componentFunction, allowOneC
|
|
|
33262
34031
|
if (isInsideInlineEventHandler(node, componentFunction)) return true;
|
|
33263
34032
|
let current = node.parent;
|
|
33264
34033
|
while (current && current !== componentFunction) {
|
|
33265
|
-
if (isFunctionLike$
|
|
34034
|
+
if (isFunctionLike$1(current)) {
|
|
33266
34035
|
if (isFunctionWiredToEventAttribute(analysis, current) && !isFunctionUsedOutsideHandlers(analysis, current, componentFunction)) return true;
|
|
33267
34036
|
return allowOneCallFrame && isFunctionCalledOnlyFromHandlers(analysis, current, componentFunction);
|
|
33268
34037
|
}
|
|
@@ -33275,7 +34044,7 @@ const isSetterWriterUsage = (identifier) => {
|
|
|
33275
34044
|
if (!parent) return false;
|
|
33276
34045
|
if (isNodeOfType(parent, "CallExpression")) return parent.callee === identifier || (parent.arguments ?? []).includes(identifier);
|
|
33277
34046
|
let current = identifier;
|
|
33278
|
-
while (current && !isFunctionLike$
|
|
34047
|
+
while (current && !isFunctionLike$1(current)) {
|
|
33279
34048
|
if (isEventAttribute(current) || isEventProperty(current)) return true;
|
|
33280
34049
|
current = current.parent;
|
|
33281
34050
|
}
|
|
@@ -33329,7 +34098,7 @@ const consequentHasTransferableWork = (analysis, consequent) => {
|
|
|
33329
34098
|
let hasTransferableWork = false;
|
|
33330
34099
|
walkAst(consequent, (child) => {
|
|
33331
34100
|
if (hasTransferableWork) return false;
|
|
33332
|
-
if (child !== consequent && isFunctionLike$
|
|
34101
|
+
if (child !== consequent && isFunctionLike$1(child)) return false;
|
|
33333
34102
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
33334
34103
|
if (isNodeOfType(child.callee, "Identifier")) {
|
|
33335
34104
|
const calleeReference = getRef(analysis, child.callee);
|
|
@@ -33356,7 +34125,7 @@ const consequentHasAdditionalReactiveGuard = (analysis, consequent, handlerState
|
|
|
33356
34125
|
let hasAdditionalGuard = false;
|
|
33357
34126
|
walkAst(consequent, (child) => {
|
|
33358
34127
|
if (hasAdditionalGuard) return false;
|
|
33359
|
-
if (child !== consequent && isFunctionLike$
|
|
34128
|
+
if (child !== consequent && isFunctionLike$1(child)) return false;
|
|
33360
34129
|
if (!isNodeOfType(child, "IfStatement")) return;
|
|
33361
34130
|
if (guardHasOtherReactiveSource(analysis, child.test, handlerStateDeclarator)) {
|
|
33362
34131
|
hasAdditionalGuard = true;
|
|
@@ -33380,7 +34149,7 @@ const noEventHandler = defineRule({
|
|
|
33380
34149
|
let reported = false;
|
|
33381
34150
|
walkAst(frame.functionNode, (child) => {
|
|
33382
34151
|
if (reported) return false;
|
|
33383
|
-
if (child !== frame.functionNode && isFunctionLike$
|
|
34152
|
+
if (child !== frame.functionNode && isFunctionLike$1(child)) return false;
|
|
33384
34153
|
if (!isNodeOfType(child, "IfStatement") || child.alternate) return;
|
|
33385
34154
|
if (!consequentHasTransferableWork(analysis, child.consequent)) return;
|
|
33386
34155
|
const stateReferences = collectGuardStateReferences(analysis, child.test);
|
|
@@ -33694,7 +34463,7 @@ const addIteratorMutationDependencies = (graph, expression, scope, eventHandlerR
|
|
|
33694
34463
|
for (const argument of expression.arguments ?? []) {
|
|
33695
34464
|
if (!isNodeOfType(argument, "ArrowFunctionExpression") && !isNodeOfType(argument, "FunctionExpression")) continue;
|
|
33696
34465
|
walkAst(argument.body, (node) => {
|
|
33697
|
-
if (node !== argument.body && isFunctionLike$
|
|
34466
|
+
if (node !== argument.body && isFunctionLike$1(node)) return false;
|
|
33698
34467
|
if (!isNodeOfType(node, "CallExpression")) return;
|
|
33699
34468
|
if (!isNodeOfType(node.callee, "MemberExpression")) return;
|
|
33700
34469
|
const nestedMethodName = getStaticMemberPropertyName(node.callee);
|
|
@@ -34155,12 +34924,12 @@ const isNetworkRequest = (node) => isRealFetchCall(node) || isXmlHttpRequestCons
|
|
|
34155
34924
|
const resolveLocalFunction = (expression, context) => {
|
|
34156
34925
|
if (!expression) return null;
|
|
34157
34926
|
const unwrappedExpression = stripParenExpression(expression);
|
|
34158
|
-
if (isFunctionLike$
|
|
34927
|
+
if (isFunctionLike$1(unwrappedExpression)) return unwrappedExpression;
|
|
34159
34928
|
if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
|
|
34160
34929
|
const initializer = context.scopes.symbolFor(unwrappedExpression)?.initializer;
|
|
34161
34930
|
if (!initializer) return null;
|
|
34162
34931
|
const unwrappedInitializer = stripParenExpression(initializer);
|
|
34163
|
-
return isFunctionLike$
|
|
34932
|
+
return isFunctionLike$1(unwrappedInitializer) ? unwrappedInitializer : null;
|
|
34164
34933
|
};
|
|
34165
34934
|
const collectEffectAnalysisFunctions = (effectCallback, context) => {
|
|
34166
34935
|
const analysisFunctions = /* @__PURE__ */ new Set();
|
|
@@ -34177,7 +34946,7 @@ const collectEffectAnalysisFunctions = (effectCallback, context) => {
|
|
|
34177
34946
|
const currentFunction = pendingFunctions.pop();
|
|
34178
34947
|
if (!currentFunction) break;
|
|
34179
34948
|
walkAst(currentFunction, (child) => {
|
|
34180
|
-
if (child !== currentFunction && isFunctionLike$
|
|
34949
|
+
if (child !== currentFunction && isFunctionLike$1(child)) return false;
|
|
34181
34950
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
34182
34951
|
const calledFunction = resolveLocalFunction(child.callee, context);
|
|
34183
34952
|
if (calledFunction) enqueueFunction(calledFunction);
|
|
@@ -34193,7 +34962,7 @@ const collectNodesFromAnalysisFunctions = (analysisFunctions, predicate) => {
|
|
|
34193
34962
|
const nodes = [];
|
|
34194
34963
|
const seenNodes = /* @__PURE__ */ new Set();
|
|
34195
34964
|
for (const analysisFunction of analysisFunctions) walkAst(analysisFunction, (child) => {
|
|
34196
|
-
if (child !== analysisFunction && isFunctionLike$
|
|
34965
|
+
if (child !== analysisFunction && isFunctionLike$1(child)) return false;
|
|
34197
34966
|
if (!seenNodes.has(child) && predicate(child)) {
|
|
34198
34967
|
seenNodes.add(child);
|
|
34199
34968
|
nodes.push(child);
|
|
@@ -34202,19 +34971,19 @@ const collectNodesFromAnalysisFunctions = (analysisFunctions, predicate) => {
|
|
|
34202
34971
|
return nodes;
|
|
34203
34972
|
};
|
|
34204
34973
|
const findEffectCleanupFunction = (callback) => {
|
|
34205
|
-
if (!isFunctionLike$
|
|
34974
|
+
if (!isFunctionLike$1(callback)) return null;
|
|
34206
34975
|
const body = callback.body;
|
|
34207
|
-
if (isFunctionLike$
|
|
34976
|
+
if (isFunctionLike$1(body)) return body;
|
|
34208
34977
|
let cleanup = null;
|
|
34209
34978
|
walkAst(body, (child) => {
|
|
34210
|
-
if (child !== body && isFunctionLike$
|
|
34211
|
-
if (isNodeOfType(child, "ReturnStatement") && child.argument && isFunctionLike$
|
|
34979
|
+
if (child !== body && isFunctionLike$1(child)) return false;
|
|
34980
|
+
if (isNodeOfType(child, "ReturnStatement") && child.argument && isFunctionLike$1(child.argument)) cleanup = child.argument;
|
|
34212
34981
|
});
|
|
34213
34982
|
return cleanup;
|
|
34214
34983
|
};
|
|
34215
34984
|
const collectEffectCancellationFlagKeys = (effectCallback, context) => {
|
|
34216
34985
|
const flagKeys = /* @__PURE__ */ new Set();
|
|
34217
|
-
if (!isFunctionLike$
|
|
34986
|
+
if (!isFunctionLike$1(effectCallback)) return flagKeys;
|
|
34218
34987
|
const body = effectCallback.body;
|
|
34219
34988
|
if (!isNodeOfType(body, "BlockStatement")) return flagKeys;
|
|
34220
34989
|
for (const statement of body.body ?? []) {
|
|
@@ -34298,7 +35067,7 @@ const isCompletionSinkInsideCancellationGuard = (completionSink, cancellationFla
|
|
|
34298
35067
|
let currentNode = completionSink;
|
|
34299
35068
|
let parentNode = currentNode.parent;
|
|
34300
35069
|
while (parentNode) {
|
|
34301
|
-
if (isFunctionLike$
|
|
35070
|
+
if (isFunctionLike$1(parentNode)) return false;
|
|
34302
35071
|
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
34303
35072
|
const cancellationResult = readCancellationCondition(parentNode.test, cancellationFlagKey, context);
|
|
34304
35073
|
if (currentNode === parentNode.consequent && cancellationResult === false) return true;
|
|
@@ -34323,7 +35092,7 @@ const isCompletionSinkAfterCancellationEarlyExit = (completionSink, cancellation
|
|
|
34323
35092
|
let currentNode = completionSink;
|
|
34324
35093
|
let parentNode = currentNode.parent;
|
|
34325
35094
|
while (parentNode) {
|
|
34326
|
-
if (isFunctionLike$
|
|
35095
|
+
if (isFunctionLike$1(parentNode)) return false;
|
|
34327
35096
|
if (isNodeOfType(parentNode, "BlockStatement")) {
|
|
34328
35097
|
const statementIndex = parentNode.body.findIndex((statement) => statement === currentNode);
|
|
34329
35098
|
if (statementIndex >= 0) for (const statement of parentNode.body.slice(0, statementIndex)) {
|
|
@@ -34494,7 +35263,7 @@ const collectMeasuringFunctionNames = (program) => {
|
|
|
34494
35263
|
if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier")) return;
|
|
34495
35264
|
let functionValue = child.init;
|
|
34496
35265
|
if (functionValue && isNodeOfType(functionValue, "CallExpression") && isNodeOfType(functionValue.callee, "Identifier") && /^use[A-Z]/.test(functionValue.callee.name)) functionValue = functionValue.arguments?.[0];
|
|
34497
|
-
if (functionValue && isFunctionLike$
|
|
35266
|
+
if (functionValue && isFunctionLike$1(functionValue) && subtreeReadsDomMeasurement(functionValue.body)) names.add(child.id.name);
|
|
34498
35267
|
});
|
|
34499
35268
|
return names;
|
|
34500
35269
|
};
|
|
@@ -34521,7 +35290,7 @@ const isInsideStartViewTransition = (node) => {
|
|
|
34521
35290
|
const enclosingFunctionChainReadsMeasurement = (node, measuringFunctionNames) => {
|
|
34522
35291
|
let cursor = node.parent;
|
|
34523
35292
|
while (cursor) {
|
|
34524
|
-
if (isFunctionLike$
|
|
35293
|
+
if (isFunctionLike$1(cursor)) {
|
|
34525
35294
|
const body = cursor.body;
|
|
34526
35295
|
if (subtreeReadsDomMeasurement(body) || callsAnyName(body, measuringFunctionNames)) return true;
|
|
34527
35296
|
}
|
|
@@ -34963,7 +35732,7 @@ const hasClientRenderEvidence = (componentOrHookNode, fileHasUseClientDirective)
|
|
|
34963
35732
|
const displayName = componentOrHookDisplayNameForFunction(componentOrHookNode);
|
|
34964
35733
|
if (displayName && isReactHookName(displayName)) return true;
|
|
34965
35734
|
let callsHook = false;
|
|
34966
|
-
walkAst((isFunctionLike$
|
|
35735
|
+
walkAst((isFunctionLike$1(componentOrHookNode) ? componentOrHookNode.body : null) ?? componentOrHookNode, (child) => {
|
|
34967
35736
|
if (callsHook) return false;
|
|
34968
35737
|
if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && isReactHookName(child.callee.name)) {
|
|
34969
35738
|
callsHook = true;
|
|
@@ -35033,7 +35802,7 @@ const readInitialStateBoolean = (expression, scopes) => readInitialStateBooleanI
|
|
|
35033
35802
|
//#endregion
|
|
35034
35803
|
//#region src/plugin/utils/is-after-client-only-early-return.ts
|
|
35035
35804
|
const isAfterClientOnlyEarlyReturn = (node, componentOrHookNode, scopes) => {
|
|
35036
|
-
const body = isFunctionLike$
|
|
35805
|
+
const body = isFunctionLike$1(componentOrHookNode) ? componentOrHookNode.body : null;
|
|
35037
35806
|
if (!isNodeOfType(body, "BlockStatement")) return false;
|
|
35038
35807
|
const ancestors = /* @__PURE__ */ new Set();
|
|
35039
35808
|
let currentNode = node;
|
|
@@ -35066,7 +35835,7 @@ const isGatedByFalsyInitialState = (node, scopes) => {
|
|
|
35066
35835
|
};
|
|
35067
35836
|
//#endregion
|
|
35068
35837
|
//#region src/plugin/rules/performance/no-hydration-branch-on-browser-global.ts
|
|
35069
|
-
const evaluateEquality = (operator, left, right) => {
|
|
35838
|
+
const evaluateEquality$1 = (operator, left, right) => {
|
|
35070
35839
|
if (operator === "===" || operator === "==") return left === right;
|
|
35071
35840
|
if (operator === "!==" || operator === "!=") return left !== right;
|
|
35072
35841
|
return null;
|
|
@@ -35097,8 +35866,8 @@ const matchBrowserPredicate = (expression, context) => {
|
|
|
35097
35866
|
const browserGlobalName = leftGlobalName && typeof rightString === "string" ? leftGlobalName : rightGlobalName && typeof leftString === "string" ? rightGlobalName : null;
|
|
35098
35867
|
const comparedType = leftGlobalName && typeof rightString === "string" ? rightString : rightGlobalName && typeof leftString === "string" ? leftString : null;
|
|
35099
35868
|
if (!browserGlobalName || !comparedType) return null;
|
|
35100
|
-
const clientResult = evaluateEquality(unwrappedExpression.operator, "object", comparedType);
|
|
35101
|
-
const serverResult = evaluateEquality(unwrappedExpression.operator, "undefined", comparedType);
|
|
35869
|
+
const clientResult = evaluateEquality$1(unwrappedExpression.operator, "object", comparedType);
|
|
35870
|
+
const serverResult = evaluateEquality$1(unwrappedExpression.operator, "undefined", comparedType);
|
|
35102
35871
|
if (clientResult === null || serverResult === null || clientResult === serverResult) return null;
|
|
35103
35872
|
return {
|
|
35104
35873
|
browserGlobalName,
|
|
@@ -35197,7 +35966,7 @@ const findEnclosingJsxAttribute = (node) => {
|
|
|
35197
35966
|
let currentNode = node.parent;
|
|
35198
35967
|
while (currentNode) {
|
|
35199
35968
|
if (isNodeOfType(currentNode, "JSXAttribute")) return currentNode;
|
|
35200
|
-
if (isNodeOfType(currentNode, "JSXElement") || isNodeOfType(currentNode, "JSXFragment") || isFunctionLike$
|
|
35969
|
+
if (isNodeOfType(currentNode, "JSXElement") || isNodeOfType(currentNode, "JSXFragment") || isFunctionLike$1(currentNode)) return null;
|
|
35201
35970
|
currentNode = currentNode.parent;
|
|
35202
35971
|
}
|
|
35203
35972
|
return null;
|
|
@@ -35213,8 +35982,8 @@ const isInRenderedOutput = (node, componentOrHookNode, scopes) => {
|
|
|
35213
35982
|
if (isNodeOfType(parentNode, "ReturnStatement")) {
|
|
35214
35983
|
if (findEnclosingFunction$1(parentNode) === componentOrHookNode) return true;
|
|
35215
35984
|
}
|
|
35216
|
-
if (parentNode === componentOrHookNode) return isFunctionLike$
|
|
35217
|
-
if (isFunctionLike$
|
|
35985
|
+
if (parentNode === componentOrHookNode) return isFunctionLike$1(componentOrHookNode) && !isNodeOfType(componentOrHookNode.body, "BlockStatement") && componentOrHookNode.body === currentNode;
|
|
35986
|
+
if (isFunctionLike$1(parentNode) && !executesDuringRender(parentNode, scopes)) return false;
|
|
35218
35987
|
currentNode = parentNode;
|
|
35219
35988
|
parentNode = currentNode.parent;
|
|
35220
35989
|
}
|
|
@@ -35492,7 +36261,7 @@ const findImpureUpdaterOperation = (updater, scopes) => {
|
|
|
35492
36261
|
let operation = null;
|
|
35493
36262
|
walkAst(updater, (child) => {
|
|
35494
36263
|
if (operation) return false;
|
|
35495
|
-
if (child !== updater && isFunctionLike$
|
|
36264
|
+
if (child !== updater && isFunctionLike$1(child) && !isDefinitelySynchronousCallback(child, scopes)) return false;
|
|
35496
36265
|
if (isNodeOfType(child, "CallExpression")) {
|
|
35497
36266
|
if (isNodeOfType(child.callee, "Identifier") && analysis) {
|
|
35498
36267
|
const calleeReference = getRef(analysis, child.callee);
|
|
@@ -35533,7 +36302,7 @@ const noImpureStateUpdater = defineRule({
|
|
|
35533
36302
|
const stateDeclarator = getUseStateDecl(analysis, calleeReference);
|
|
35534
36303
|
if (!isNodeOfType(stateDeclarator, "VariableDeclarator") || !isNodeOfType(stateDeclarator.init, "CallExpression") || !isReactApiCall(stateDeclarator.init, "useState", context.scopes, { allowGlobalReactNamespace: true })) return;
|
|
35535
36304
|
let updater = null;
|
|
35536
|
-
if (isFunctionLike$
|
|
36305
|
+
if (isFunctionLike$1(updaterArgument)) updater = updaterArgument;
|
|
35537
36306
|
else if (isNodeOfType(updaterArgument, "Identifier")) {
|
|
35538
36307
|
const updaterReference = getRef(analysis, updaterArgument);
|
|
35539
36308
|
if (updaterReference) updater = resolveToFunction(updaterReference);
|
|
@@ -35966,7 +36735,7 @@ const isCatchParameterRoundTrip = (stringifyCall) => {
|
|
|
35966
36735
|
if (!argument || !isNodeOfType(argument, "Identifier")) return false;
|
|
35967
36736
|
let current = stringifyCall.parent;
|
|
35968
36737
|
while (current) {
|
|
35969
|
-
if (isFunctionLike$
|
|
36738
|
+
if (isFunctionLike$1(current) && (current.params ?? []).some((parameter) => isNodeOfType(parameter, "Identifier") && parameter.name === argument.name)) return false;
|
|
35970
36739
|
if (isNodeOfType(current, "CatchClause") && isNodeOfType(current.param, "Identifier") && current.param.name === argument.name) return true;
|
|
35971
36740
|
current = current.parent ?? null;
|
|
35972
36741
|
}
|
|
@@ -35980,7 +36749,7 @@ const getName = (candidate) => {
|
|
|
35980
36749
|
const isInsideSnapshotHelper = (node) => {
|
|
35981
36750
|
let current = node.parent;
|
|
35982
36751
|
while (current) {
|
|
35983
|
-
if (isFunctionLike$
|
|
36752
|
+
if (isFunctionLike$1(current)) {
|
|
35984
36753
|
const directName = isNodeOfType(current, "ArrowFunctionExpression") ? null : getName(current.id);
|
|
35985
36754
|
const parent = current.parent;
|
|
35986
36755
|
let boundName = directName;
|
|
@@ -36003,9 +36772,9 @@ const noJsonParseStringifyClone = defineRule({
|
|
|
36003
36772
|
const firstArgument = node.arguments?.[0];
|
|
36004
36773
|
if (!firstArgument || !isJsonMethodCall(firstArgument, "stringify")) return;
|
|
36005
36774
|
const replacer = firstArgument.arguments?.[1];
|
|
36006
|
-
if (isFunctionLike$
|
|
36775
|
+
if (isFunctionLike$1(replacer) || isNodeOfType(replacer, "ArrayExpression")) return;
|
|
36007
36776
|
const reviver = node.arguments?.[1];
|
|
36008
|
-
if (isFunctionLike$
|
|
36777
|
+
if (isFunctionLike$1(reviver)) return;
|
|
36009
36778
|
if (isInsideSnapshotHelper(node)) return;
|
|
36010
36779
|
if (isAssignedToNormalizationBinding(node)) return;
|
|
36011
36780
|
if (isCatchParameterRoundTrip(firstArgument)) return;
|
|
@@ -36332,11 +37101,11 @@ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @
|
|
|
36332
37101
|
//#endregion
|
|
36333
37102
|
//#region src/plugin/utils/function-contains-proven-react-hook-call.ts
|
|
36334
37103
|
const functionContainsProvenReactHookCall = (functionNode, scopes) => {
|
|
36335
|
-
if (!isFunctionLike$
|
|
37104
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
36336
37105
|
let containsReactHookCall = false;
|
|
36337
37106
|
walkAst(functionNode.body, (node) => {
|
|
36338
37107
|
if (containsReactHookCall) return false;
|
|
36339
|
-
if (node !== functionNode.body && (isFunctionLike$
|
|
37108
|
+
if (node !== functionNode.body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
|
|
36340
37109
|
if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
|
|
36341
37110
|
containsReactHookCall = true;
|
|
36342
37111
|
return false;
|
|
@@ -36347,7 +37116,7 @@ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
|
|
|
36347
37116
|
//#endregion
|
|
36348
37117
|
//#region src/plugin/utils/function-returns-props-children.ts
|
|
36349
37118
|
const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
|
|
36350
|
-
if (!isFunctionLike$
|
|
37119
|
+
if (!isFunctionLike$1(functionNode) || functionNode.params.length === 0) return false;
|
|
36351
37120
|
const firstParameter = stripParenExpression(functionNode.params[0]);
|
|
36352
37121
|
const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
|
|
36353
37122
|
const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
|
|
@@ -36383,7 +37152,7 @@ const isNullExpression = (expression) => {
|
|
|
36383
37152
|
return isNodeOfType(candidate, "Literal") && candidate.value === null;
|
|
36384
37153
|
};
|
|
36385
37154
|
const functionReturnsOnlyNull = (functionNode) => {
|
|
36386
|
-
if (!isFunctionLike$
|
|
37155
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
36387
37156
|
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
|
|
36388
37157
|
const returnStatements = collectFunctionReturnStatements(functionNode);
|
|
36389
37158
|
return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
|
|
@@ -37024,10 +37793,10 @@ const noLocaleFormatInRender = defineRule({
|
|
|
37024
37793
|
const componentOrHookNode = findRenderPhaseComponentOrHook(node, context.scopes);
|
|
37025
37794
|
if (!componentOrHookNode) return;
|
|
37026
37795
|
const helperNode = findVariableInitializer(expression.callee, helperName)?.initializer;
|
|
37027
|
-
if (!helperNode || !isFunctionLike$
|
|
37796
|
+
if (!helperNode || !isFunctionLike$1(helperNode)) return;
|
|
37028
37797
|
if (componentOrHookDisplayNameForFunction(helperNode)) return;
|
|
37029
37798
|
walkAst(helperNode.body ?? helperNode, (child) => {
|
|
37030
|
-
if (isFunctionLike$
|
|
37799
|
+
if (isFunctionLike$1(child)) return false;
|
|
37031
37800
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
37032
37801
|
const match = matchLocaleMethodCall(child, context.scopes) ?? matchIntlFormatCall(child, context.scopes);
|
|
37033
37802
|
if (!match || reportedNodes.has(match.node)) return;
|
|
@@ -37250,7 +38019,7 @@ const findDirectMatchMediaCall = (root, context) => {
|
|
|
37250
38019
|
let matchMediaCall = null;
|
|
37251
38020
|
walkAst(root, (visitedNode) => {
|
|
37252
38021
|
if (matchMediaCall) return false;
|
|
37253
|
-
if (isFunctionLike$
|
|
38022
|
+
if (isFunctionLike$1(visitedNode)) return false;
|
|
37254
38023
|
if (isGlobalMatchMediaCall(visitedNode, context.scopes)) {
|
|
37255
38024
|
matchMediaCall = visitedNode;
|
|
37256
38025
|
return false;
|
|
@@ -37260,7 +38029,7 @@ const findDirectMatchMediaCall = (root, context) => {
|
|
|
37260
38029
|
};
|
|
37261
38030
|
const findMatchMediaCallDuringInitialization = (initializer, context) => {
|
|
37262
38031
|
const rootInitializer = stripParenExpression(initializer);
|
|
37263
|
-
if (!isFunctionLike$
|
|
38032
|
+
if (!isFunctionLike$1(rootInitializer)) return findDirectMatchMediaCall(rootInitializer, context);
|
|
37264
38033
|
if (rootInitializer.async || rootInitializer.generator) return null;
|
|
37265
38034
|
return findDirectMatchMediaCall(rootInitializer.body, context);
|
|
37266
38035
|
};
|
|
@@ -37921,7 +38690,7 @@ const isLodashMutatorCall = (callExpression) => {
|
|
|
37921
38690
|
const resolveReducerFunction = (node, currentFilename) => {
|
|
37922
38691
|
if (!node) return null;
|
|
37923
38692
|
const unwrappedNode = stripParenExpression(node);
|
|
37924
|
-
if (isFunctionLike$
|
|
38693
|
+
if (isFunctionLike$1(unwrappedNode)) return {
|
|
37925
38694
|
functionNode: unwrappedNode,
|
|
37926
38695
|
crossFileSourceDisplay: null
|
|
37927
38696
|
};
|
|
@@ -37929,7 +38698,7 @@ const resolveReducerFunction = (node, currentFilename) => {
|
|
|
37929
38698
|
const initializer = findVariableInitializer(unwrappedNode, unwrappedNode.name)?.initializer;
|
|
37930
38699
|
if (!initializer) return null;
|
|
37931
38700
|
const unwrappedInitializer = stripParenExpression(initializer);
|
|
37932
|
-
if (isFunctionLike$
|
|
38701
|
+
if (isFunctionLike$1(unwrappedInitializer)) return {
|
|
37933
38702
|
functionNode: unwrappedInitializer,
|
|
37934
38703
|
crossFileSourceDisplay: null
|
|
37935
38704
|
};
|
|
@@ -38040,11 +38809,11 @@ const canExpressionReturnOriginalReducerStateReference = (node, state) => {
|
|
|
38040
38809
|
return false;
|
|
38041
38810
|
};
|
|
38042
38811
|
const collectReducerStateMutationsInExpressionOrStatement = (node, state) => {
|
|
38043
|
-
if (isFunctionLike$
|
|
38812
|
+
if (isFunctionLike$1(node)) return [];
|
|
38044
38813
|
const mutations = [];
|
|
38045
38814
|
walkAst(node, (child) => {
|
|
38046
38815
|
const unwrappedChild = stripParenExpression(child);
|
|
38047
|
-
if (child !== node && isFunctionLike$
|
|
38816
|
+
if (child !== node && isFunctionLike$1(unwrappedChild)) return false;
|
|
38048
38817
|
if (isNodeOfType(unwrappedChild, "AssignmentExpression")) {
|
|
38049
38818
|
if (isNodeOfType(stripParenExpression(unwrappedChild.left), "MemberExpression") && isExpressionRootedInMutableReducerStateSource(unwrappedChild.left, state)) mutations.push({ node: unwrappedChild });
|
|
38050
38819
|
return;
|
|
@@ -38150,7 +38919,7 @@ const updateReducerStateIdentityForIdentifierAssignment = (assignment, state) =>
|
|
|
38150
38919
|
if (isExpressionReachableFromOriginalReducerState(assignment.right, state)) state.mutableStateSourceNames.add(name);
|
|
38151
38920
|
};
|
|
38152
38921
|
const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, reportedNodes, options) => {
|
|
38153
|
-
if (!isFunctionLike$
|
|
38922
|
+
if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return;
|
|
38154
38923
|
const firstParam = functionNode.params?.[0];
|
|
38155
38924
|
const stateName = isNodeOfType(firstParam, "Identifier") ? firstParam.name : isNodeOfType(firstParam, "AssignmentPattern") && isNodeOfType(firstParam.left, "Identifier") ? firstParam.left.name : null;
|
|
38156
38925
|
if (!stateName) return;
|
|
@@ -38819,7 +39588,7 @@ const rendersFocusManagerInSameFunction = (styleAttribute) => {
|
|
|
38819
39588
|
let ancestor = styleAttribute.parent;
|
|
38820
39589
|
while (ancestor) {
|
|
38821
39590
|
scopeOwner = ancestor;
|
|
38822
|
-
if (isFunctionLike$
|
|
39591
|
+
if (isFunctionLike$1(ancestor)) break;
|
|
38823
39592
|
ancestor = ancestor.parent ?? null;
|
|
38824
39593
|
}
|
|
38825
39594
|
let didFindFocusManager = false;
|
|
@@ -39156,7 +39925,7 @@ const getWrapperHookWrappedFunction = (initializer) => {
|
|
|
39156
39925
|
const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") ? callee.property.name : null;
|
|
39157
39926
|
if (!calleeName || !FUNCTION_WRAPPER_HOOK_NAMES$1.has(calleeName)) return null;
|
|
39158
39927
|
const wrapped = initializer.arguments?.[0];
|
|
39159
|
-
if (!wrapped || !isFunctionLike$
|
|
39928
|
+
if (!wrapped || !isFunctionLike$1(wrapped)) return null;
|
|
39160
39929
|
return wrapped;
|
|
39161
39930
|
};
|
|
39162
39931
|
const HANDLER_NAMED_PROP_PATTERN = /^(on|handle)[A-Z]/;
|
|
@@ -39310,7 +40079,7 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
|
|
|
39310
40079
|
if (!bindingProvenance) return null;
|
|
39311
40080
|
const { refCall, variables } = bindingProvenance;
|
|
39312
40081
|
const componentFunction = findEnclosingFunction$1(effectCall);
|
|
39313
|
-
if (!componentFunction || !isFunctionLike$
|
|
40082
|
+
if (!componentFunction || !isFunctionLike$1(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
|
|
39314
40083
|
if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
|
|
39315
40084
|
const callbackPropNames = /* @__PURE__ */ new Set();
|
|
39316
40085
|
const initializer = refCall.arguments?.[0];
|
|
@@ -39348,7 +40117,7 @@ const isHandlerBagArgument = (analysis, argument) => {
|
|
|
39348
40117
|
return properties.every((property) => {
|
|
39349
40118
|
if (!isNodeOfType(property, "Property")) return false;
|
|
39350
40119
|
const value = property.value;
|
|
39351
|
-
if (isFunctionLike$
|
|
40120
|
+
if (isFunctionLike$1(value)) return true;
|
|
39352
40121
|
if (isNodeOfType(value, "Identifier")) {
|
|
39353
40122
|
const valueRef = getRef(analysis, value);
|
|
39354
40123
|
return Boolean(valueRef && resolveToFunction(valueRef));
|
|
@@ -39451,7 +40220,7 @@ const noPassDataToParent = defineRule({
|
|
|
39451
40220
|
const isSetterNamedCallee = callbackRefProvenance ? [...callbackRefProvenance.callbackPropNames].every((callbackPropName) => SETTER_NAMED_PROP_PATTERN.test(callbackPropName)) : Boolean((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) && SETTER_NAMED_PROP_PATTERN.test((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) ?? ""));
|
|
39452
40221
|
const isLeafRef = (argRef) => getUpstreamRefs(analysis, argRef).length === 1;
|
|
39453
40222
|
const argsUpstreamRefs = (callExpr.arguments ?? []).flatMap((argument) => {
|
|
39454
|
-
if (isFunctionLike$
|
|
40223
|
+
if (isFunctionLike$1(argument)) {
|
|
39455
40224
|
if (!isSetterNamedCallee) return [];
|
|
39456
40225
|
return getFunctionalUpdaterDataRefs(analysis, argument);
|
|
39457
40226
|
}
|
|
@@ -39568,7 +40337,7 @@ const collectPropCallbackBoundStateRefs = (analysis, ref, isPropCallbackRef) =>
|
|
|
39568
40337
|
if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
|
|
39569
40338
|
if (!(getEventualCallRefsTo(analysis, upRef, isPropCallbackRef).length > 0)) continue;
|
|
39570
40339
|
for (const argument of callExpr.arguments ?? []) {
|
|
39571
|
-
if (isFunctionLike$
|
|
40340
|
+
if (isFunctionLike$1(argument)) continue;
|
|
39572
40341
|
for (const argRef of getDownstreamRefs(analysis, argument)) {
|
|
39573
40342
|
if (resolveToFunction(argRef)) continue;
|
|
39574
40343
|
collectUpstreamStateRefs(analysis, argRef, stateRefs, /* @__PURE__ */ new Set());
|
|
@@ -39585,7 +40354,7 @@ const isSetterNamedCallbackReceivingData = (callbackRef) => {
|
|
|
39585
40354
|
if (!isCustomHookParameter(callbackRef)) return false;
|
|
39586
40355
|
const firstArgument = (callExpr.arguments ?? [])[0];
|
|
39587
40356
|
if (!firstArgument) return false;
|
|
39588
|
-
return !isNodeOfType(firstArgument, "Literal") && !isNodeOfType(firstArgument, "TemplateLiteral") && !isFunctionLike$
|
|
40357
|
+
return !isNodeOfType(firstArgument, "Literal") && !isNodeOfType(firstArgument, "TemplateLiteral") && !isFunctionLike$1(firstArgument);
|
|
39589
40358
|
};
|
|
39590
40359
|
const FUNCTION_WRAPPER_HOOK_NAMES = new Set([
|
|
39591
40360
|
"useCallback",
|
|
@@ -39765,14 +40534,25 @@ const isPropsChildrenLength = (node, scopes) => {
|
|
|
39765
40534
|
const unwrappedNode = stripParenExpression(node);
|
|
39766
40535
|
return isNodeOfType(unwrappedNode, "MemberExpression") && !unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Identifier") && unwrappedNode.property.name === "length" && resolvesToPropsChildren(stripParenExpression(unwrappedNode.object), scopes);
|
|
39767
40536
|
};
|
|
40537
|
+
const resolveStaticNumericValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40538
|
+
const unwrappedNode = stripParenExpression(node);
|
|
40539
|
+
if (isNodeOfType(unwrappedNode, "Literal") && typeof unwrappedNode.value === "number") return Number.isFinite(unwrappedNode.value) ? unwrappedNode.value : null;
|
|
40540
|
+
if (!isNodeOfType(unwrappedNode, "Identifier")) return null;
|
|
40541
|
+
const symbol = scopes.symbolFor(unwrappedNode);
|
|
40542
|
+
if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
|
|
40543
|
+
const initializer = getDirectConstInitializer(symbol);
|
|
40544
|
+
if (!initializer) return null;
|
|
40545
|
+
return resolveStaticNumericValue(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id));
|
|
40546
|
+
};
|
|
39768
40547
|
const isLargeTextLengthComparison = (node, scopes) => {
|
|
39769
40548
|
const unwrappedNode = stripParenExpression(node);
|
|
39770
40549
|
if (!isNodeOfType(unwrappedNode, "BinaryExpression")) return false;
|
|
39771
40550
|
const leftIsLength = isPropsChildrenLength(unwrappedNode.left, scopes);
|
|
39772
40551
|
const rightIsLength = isPropsChildrenLength(unwrappedNode.right, scopes);
|
|
39773
40552
|
const thresholdNode = leftIsLength ? unwrappedNode.right : unwrappedNode.left;
|
|
39774
|
-
if (!leftIsLength && !rightIsLength
|
|
39775
|
-
|
|
40553
|
+
if (!leftIsLength && !rightIsLength) return false;
|
|
40554
|
+
const thresholdValue = resolveStaticNumericValue(thresholdNode, scopes);
|
|
40555
|
+
if (thresholdValue === null || thresholdValue < 1e3) return false;
|
|
39776
40556
|
return leftIsLength ? unwrappedNode.operator === ">" || unwrappedNode.operator === ">=" : unwrappedNode.operator === "<" || unwrappedNode.operator === "<=";
|
|
39777
40557
|
};
|
|
39778
40558
|
const isLargeStringOptimizationGuard = (comparison, scopes) => {
|
|
@@ -39859,7 +40639,7 @@ const collectEnclosingParameterNames = (handlerExpression) => {
|
|
|
39859
40639
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
39860
40640
|
let ancestor = handlerExpression;
|
|
39861
40641
|
while (ancestor) {
|
|
39862
|
-
if (isFunctionLike$
|
|
40642
|
+
if (isFunctionLike$1(ancestor)) for (const parameter of ancestor.params ?? []) collectPatternNames(parameter, parameterNames);
|
|
39863
40643
|
ancestor = ancestor.parent ?? null;
|
|
39864
40644
|
}
|
|
39865
40645
|
return parameterNames;
|
|
@@ -39921,7 +40701,7 @@ const containsControlledInput = (formOpeningElement) => {
|
|
|
39921
40701
|
return didFindControlledInput;
|
|
39922
40702
|
};
|
|
39923
40703
|
const containsAsynchronousWork = (handlerExpression) => {
|
|
39924
|
-
if (isFunctionLike$
|
|
40704
|
+
if (isFunctionLike$1(handlerExpression) && handlerExpression.async === true) return true;
|
|
39925
40705
|
let didFindAsynchronousWork = false;
|
|
39926
40706
|
walkAst(handlerExpression, (child) => {
|
|
39927
40707
|
if (didFindAsynchronousWork) return;
|
|
@@ -40136,7 +40916,7 @@ const noPropCallbackInRender = defineRule({
|
|
|
40136
40916
|
const analysis = getProgramAnalysis(node);
|
|
40137
40917
|
if (!analysis) return;
|
|
40138
40918
|
const callee = stripParenExpression(node.callee);
|
|
40139
|
-
if (isFunctionLike$
|
|
40919
|
+
if (isFunctionLike$1(callee)) return;
|
|
40140
40920
|
if (!getDownstreamRefs(analysis, callee).some((reference) => isPropCallbackInvocationRef(analysis, reference))) return;
|
|
40141
40921
|
context.report({
|
|
40142
40922
|
node,
|
|
@@ -40319,7 +41099,7 @@ const findFreshCallInSubtree = (root) => {
|
|
|
40319
41099
|
let foundDescription = null;
|
|
40320
41100
|
walkAst(root, (child) => {
|
|
40321
41101
|
if (foundDescription) return false;
|
|
40322
|
-
if (child !== root && isFunctionLike$
|
|
41102
|
+
if (child !== root && isFunctionLike$1(child)) return false;
|
|
40323
41103
|
const description = isAlwaysFreshExpression(child);
|
|
40324
41104
|
if (description) {
|
|
40325
41105
|
foundDescription = description;
|
|
@@ -40863,7 +41643,7 @@ const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.
|
|
|
40863
41643
|
const hasRepeatedExecutionAncestor = (node, stop) => {
|
|
40864
41644
|
let ancestor = node.parent;
|
|
40865
41645
|
while (ancestor && ancestor !== stop) {
|
|
40866
|
-
if (isFunctionLike$
|
|
41646
|
+
if (isFunctionLike$1(ancestor) || REPEATED_ANCESTOR_TYPES.has(ancestor.type)) return true;
|
|
40867
41647
|
ancestor = ancestor.parent;
|
|
40868
41648
|
}
|
|
40869
41649
|
return ancestor !== stop;
|
|
@@ -40958,15 +41738,15 @@ const noRefCurrentInRender = defineRule({
|
|
|
40958
41738
|
const isInsideComponentContext = (node) => {
|
|
40959
41739
|
let cursor = node.parent;
|
|
40960
41740
|
while (cursor) {
|
|
40961
|
-
if (isFunctionLike$
|
|
41741
|
+
if (isFunctionLike$1(cursor) && isComponentFunction$1(cursor)) return true;
|
|
40962
41742
|
if (isEs5Component(cursor) || isEs6Component(cursor)) return true;
|
|
40963
41743
|
cursor = cursor.parent ?? null;
|
|
40964
41744
|
}
|
|
40965
41745
|
return false;
|
|
40966
41746
|
};
|
|
40967
41747
|
const functionBodyOf = (node) => {
|
|
40968
|
-
if (isFunctionLike$
|
|
40969
|
-
if (isNodeOfType(node, "VariableDeclarator") && node.init && isFunctionLike$
|
|
41748
|
+
if (isFunctionLike$1(node)) return node.body ?? null;
|
|
41749
|
+
if (isNodeOfType(node, "VariableDeclarator") && node.init && isFunctionLike$1(node.init)) return node.init.body ?? null;
|
|
40970
41750
|
return null;
|
|
40971
41751
|
};
|
|
40972
41752
|
const isHookCallee = (callee) => {
|
|
@@ -40978,7 +41758,7 @@ const containsHookCall = (body) => {
|
|
|
40978
41758
|
let found = false;
|
|
40979
41759
|
walkAst(body, (child) => {
|
|
40980
41760
|
if (found) return false;
|
|
40981
|
-
if (child !== body && isFunctionLike$
|
|
41761
|
+
if (child !== body && isFunctionLike$1(child) && isComponentFunction$1(child)) return false;
|
|
40982
41762
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
40983
41763
|
if (isHookCallee(child.callee)) found = true;
|
|
40984
41764
|
});
|
|
@@ -41100,18 +41880,107 @@ const getNodeText$1 = (node) => {
|
|
|
41100
41880
|
return value;
|
|
41101
41881
|
});
|
|
41102
41882
|
};
|
|
41103
|
-
const
|
|
41104
|
-
|
|
41105
|
-
|
|
41106
|
-
|
|
41107
|
-
|
|
41108
|
-
|
|
41109
|
-
|
|
41110
|
-
|
|
41111
|
-
|
|
41112
|
-
|
|
41883
|
+
const normalizeAsBoolean = (identity) => ({
|
|
41884
|
+
...identity,
|
|
41885
|
+
booleanNormalization: identity.booleanNormalization === "identity" ? "normalized" : identity.booleanNormalization
|
|
41886
|
+
});
|
|
41887
|
+
const negateBoolean = (identity) => ({
|
|
41888
|
+
...identity,
|
|
41889
|
+
booleanNormalization: identity.booleanNormalization === "negated" ? "normalized" : "negated"
|
|
41890
|
+
});
|
|
41891
|
+
const getLivePropExpressionIdentity = (analysis, context, node, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
41892
|
+
const expression = stripParenExpression(node);
|
|
41893
|
+
if (isNodeOfType(expression, "Identifier")) {
|
|
41894
|
+
const reference = getRef(analysis, expression);
|
|
41895
|
+
const symbol = context.scopes.symbolFor(expression);
|
|
41896
|
+
if (!reference || !symbol) return null;
|
|
41897
|
+
if (isProp(analysis, reference)) return {
|
|
41898
|
+
propSymbolId: symbol.id,
|
|
41899
|
+
memberPath: [],
|
|
41900
|
+
booleanNormalization: "identity"
|
|
41901
|
+
};
|
|
41902
|
+
if (visitedSymbolIds.has(symbol.id) || symbol.kind !== "const") return null;
|
|
41903
|
+
visitedSymbolIds.add(symbol.id);
|
|
41904
|
+
const initializer = getDirectConstInitializer(symbol);
|
|
41905
|
+
if (initializer) return getLivePropExpressionIdentity(analysis, context, initializer, visitedSymbolIds);
|
|
41906
|
+
return getUpstreamRefs(analysis, reference).some((upstreamReference) => isProp(analysis, upstreamReference)) ? {
|
|
41907
|
+
propSymbolId: symbol.id,
|
|
41908
|
+
memberPath: [],
|
|
41909
|
+
booleanNormalization: "identity"
|
|
41910
|
+
} : null;
|
|
41911
|
+
}
|
|
41912
|
+
if (isNodeOfType(expression, "MemberExpression")) {
|
|
41913
|
+
const propertyName = getStaticMemberPropertyName(expression);
|
|
41914
|
+
if (!propertyName) return null;
|
|
41915
|
+
const objectIdentity = getLivePropExpressionIdentity(analysis, context, expression.object, visitedSymbolIds);
|
|
41916
|
+
if (!objectIdentity) return null;
|
|
41917
|
+
return {
|
|
41918
|
+
...objectIdentity,
|
|
41919
|
+
memberPath: [...objectIdentity.memberPath, propertyName]
|
|
41920
|
+
};
|
|
41921
|
+
}
|
|
41922
|
+
if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee) && expression.arguments?.length === 1) {
|
|
41923
|
+
const argument = expression.arguments[0];
|
|
41924
|
+
const argumentIdentity = getLivePropExpressionIdentity(analysis, context, argument, visitedSymbolIds);
|
|
41925
|
+
return argumentIdentity ? normalizeAsBoolean(argumentIdentity) : null;
|
|
41926
|
+
}
|
|
41927
|
+
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
|
|
41928
|
+
const argumentIdentity = getLivePropExpressionIdentity(analysis, context, expression.argument, visitedSymbolIds);
|
|
41929
|
+
return argumentIdentity ? negateBoolean(argumentIdentity) : null;
|
|
41930
|
+
}
|
|
41931
|
+
return null;
|
|
41932
|
+
};
|
|
41933
|
+
const isMountSnapshotInitializer = (context, node) => {
|
|
41934
|
+
if (!isReactApiCall(node, "useMemo", context.scopes, { resolveNamedAliases: true }) || !isNodeOfType(node, "CallExpression")) return false;
|
|
41935
|
+
const dependencies = node.arguments?.[1];
|
|
41936
|
+
return isNodeOfType(dependencies, "ArrayExpression") && dependencies.elements.length === 0;
|
|
41113
41937
|
};
|
|
41114
|
-
const
|
|
41938
|
+
const isMountSnapshotBinding = (context, identifier, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
41939
|
+
const symbol = context.scopes.symbolFor(identifier);
|
|
41940
|
+
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
41941
|
+
visitedSymbolIds.add(symbol.id);
|
|
41942
|
+
const initializer = getDirectConstInitializer(symbol);
|
|
41943
|
+
if (!initializer) return false;
|
|
41944
|
+
if (isMountSnapshotInitializer(context, initializer)) return true;
|
|
41945
|
+
const expression = stripParenExpression(initializer);
|
|
41946
|
+
return isNodeOfType(expression, "Identifier") && isMountSnapshotBinding(context, expression, visitedSymbolIds);
|
|
41947
|
+
};
|
|
41948
|
+
const isConstantBooleanExpression = (context, node, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
41949
|
+
const expression = stripParenExpression(node);
|
|
41950
|
+
if (isNodeOfType(expression, "Literal")) return true;
|
|
41951
|
+
if (isNodeOfType(expression, "Identifier")) {
|
|
41952
|
+
const symbol = context.scopes.symbolFor(expression);
|
|
41953
|
+
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
41954
|
+
visitedSymbolIds.add(symbol.id);
|
|
41955
|
+
const initializer = getDirectConstInitializer(symbol);
|
|
41956
|
+
return Boolean(initializer && isConstantBooleanExpression(context, initializer, visitedSymbolIds));
|
|
41957
|
+
}
|
|
41958
|
+
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return isConstantBooleanExpression(context, expression.argument, visitedSymbolIds);
|
|
41959
|
+
if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee) && expression.arguments?.length === 1) return isConstantBooleanExpression(context, expression.arguments[0], visitedSymbolIds);
|
|
41960
|
+
return false;
|
|
41961
|
+
};
|
|
41962
|
+
const isProvenLiveBinding = (analysis, context, identifier) => {
|
|
41963
|
+
const reference = getRef(analysis, identifier);
|
|
41964
|
+
const symbol = context.scopes.symbolFor(identifier);
|
|
41965
|
+
if (!reference || !symbol || symbol.kind !== "const" || isOutsideAllFunctions(symbol)) return false;
|
|
41966
|
+
const initializer = symbol.initializer;
|
|
41967
|
+
if (!initializer || isMountSnapshotBinding(context, identifier)) return false;
|
|
41968
|
+
if (getDownstreamRefs(analysis, initializer).some((initializerReference) => isRefCurrent(initializerReference))) return false;
|
|
41969
|
+
return !isConstantBooleanExpression(context, initializer);
|
|
41970
|
+
};
|
|
41971
|
+
const areSameProvenLiveBinding = (analysis, context, left, right) => {
|
|
41972
|
+
const leftExpression = stripParenExpression(left);
|
|
41973
|
+
const rightExpression = stripParenExpression(right);
|
|
41974
|
+
if (!isNodeOfType(leftExpression, "Identifier") || !isNodeOfType(rightExpression, "Identifier")) return false;
|
|
41975
|
+
const leftSymbol = context.scopes.symbolFor(leftExpression);
|
|
41976
|
+
const rightSymbol = context.scopes.symbolFor(rightExpression);
|
|
41977
|
+
return Boolean(leftSymbol && leftSymbol === rightSymbol && isProvenLiveBinding(analysis, context, leftExpression));
|
|
41978
|
+
};
|
|
41979
|
+
const haveSameLivePropExpressionIdentity = (left, right) => {
|
|
41980
|
+
if (left.propSymbolId !== right.propSymbolId || left.booleanNormalization !== right.booleanNormalization || left.memberPath.length !== right.memberPath.length) return false;
|
|
41981
|
+
return left.memberPath.every((propertyName, index) => propertyName === right.memberPath[index]);
|
|
41982
|
+
};
|
|
41983
|
+
const isSetStateToInitialValue = (analysis, context, setterRef) => {
|
|
41115
41984
|
const callExpr = getCallExpr(setterRef);
|
|
41116
41985
|
if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) return false;
|
|
41117
41986
|
const setStateToValue = callExpr.arguments?.[0];
|
|
@@ -41122,7 +41991,12 @@ const isSetStateToInitialValue = (analysis, setterRef) => {
|
|
|
41122
41991
|
if (isUndefinedNode(setStateToValue) && isUndefinedNode(stateInitialValue)) return true;
|
|
41123
41992
|
if (setStateToValue == null && stateInitialValue == null) return true;
|
|
41124
41993
|
if (setStateToValue && !stateInitialValue || !setStateToValue && stateInitialValue) return false;
|
|
41125
|
-
if (stateInitialValue &&
|
|
41994
|
+
if (stateInitialValue && setStateToValue) {
|
|
41995
|
+
const initialLivePropIdentity = getLivePropExpressionIdentity(analysis, context, stateInitialValue);
|
|
41996
|
+
const nextLivePropIdentity = getLivePropExpressionIdentity(analysis, context, setStateToValue);
|
|
41997
|
+
if (initialLivePropIdentity && nextLivePropIdentity && haveSameLivePropExpressionIdentity(initialLivePropIdentity, nextLivePropIdentity)) return false;
|
|
41998
|
+
if (areSameProvenLiveBinding(analysis, context, stateInitialValue, setStateToValue)) return false;
|
|
41999
|
+
}
|
|
41126
42000
|
return getNodeText$1(setStateToValue) === getNodeText$1(stateInitialValue);
|
|
41127
42001
|
};
|
|
41128
42002
|
const countUseStates = (analysis, componentNode) => {
|
|
@@ -41131,10 +42005,10 @@ const countUseStates = (analysis, componentNode) => {
|
|
|
41131
42005
|
for (const ref of getDownstreamRefs(analysis, componentNode)) if (isState(analysis, ref)) stateVariables.add(ref.resolved);
|
|
41132
42006
|
return stateVariables.size;
|
|
41133
42007
|
};
|
|
41134
|
-
const findPropUsedToResetAllState = (analysis, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
|
|
42008
|
+
const findPropUsedToResetAllState = (analysis, context, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
|
|
41135
42009
|
const stateSetterRefs = effectFnRefs.filter((ref) => isSyncStateSetterCall(analysis, ref, effectFn));
|
|
41136
42010
|
if (stateSetterRefs.length === 0) return null;
|
|
41137
|
-
if (!stateSetterRefs.every((ref) => isSetStateToInitialValue(analysis, ref))) return null;
|
|
42011
|
+
if (!stateSetterRefs.every((ref) => isSetStateToInitialValue(analysis, context, ref))) return null;
|
|
41138
42012
|
if (stateSetterRefs.every((setterRef) => effectFnRefs.some((otherRef) => otherRef !== setterRef && otherRef.resolved === setterRef.resolved && Boolean(getCallExpr(otherRef)) && !isSyncStateSetterCall(analysis, otherRef, effectFn)))) return null;
|
|
41139
42013
|
const containing = findContainingNode(analysis, useEffectNode);
|
|
41140
42014
|
if (new Set(stateSetterRefs.map((setterRef) => setterRef.resolved)).size !== countUseStates(analysis, containing)) return null;
|
|
@@ -41158,7 +42032,7 @@ const noResetAllStateOnPropChange = defineRule({
|
|
|
41158
42032
|
if (containing && isCustomHook(containing)) return;
|
|
41159
42033
|
const effectFn = getEffectFn(analysis, node);
|
|
41160
42034
|
if (!effectFn) return;
|
|
41161
|
-
if (!findPropUsedToResetAllState(analysis, effectFnRefs, depsRefs, node, effectFn)) return;
|
|
42035
|
+
if (!findPropUsedToResetAllState(analysis, context, effectFnRefs, depsRefs, node, effectFn)) return;
|
|
41162
42036
|
context.report({
|
|
41163
42037
|
node,
|
|
41164
42038
|
message: `Your users briefly see stale state when a prop changes because this useEffect clears all state.`
|
|
@@ -41350,7 +42224,7 @@ const isTanStackServerFnHandler = (node) => {
|
|
|
41350
42224
|
const isInsideServerOnlyScope = (node) => {
|
|
41351
42225
|
let currentNode = node.parent ?? null;
|
|
41352
42226
|
while (currentNode) {
|
|
41353
|
-
if (isFunctionLike$
|
|
42227
|
+
if (isFunctionLike$1(currentNode)) {
|
|
41354
42228
|
if (hasUseServerDirective(currentNode) || isTanStackServerFnHandler(currentNode)) return true;
|
|
41355
42229
|
}
|
|
41356
42230
|
currentNode = currentNode.parent ?? null;
|
|
@@ -42185,7 +43059,7 @@ const isEffectCallbackFunction = (functionNode) => {
|
|
|
42185
43059
|
return isHookCall$2(parent, EFFECT_HOOK_NAMES$1) && getEffectCallback(parent) === functionNode;
|
|
42186
43060
|
};
|
|
42187
43061
|
const doesEffectCallbackReturnName = (effectCallback, name) => {
|
|
42188
|
-
if (!isFunctionLike$
|
|
43062
|
+
if (!isFunctionLike$1(effectCallback)) return false;
|
|
42189
43063
|
let isReturnedByName = false;
|
|
42190
43064
|
walkInsideStatementBlocks(effectCallback.body, (child) => {
|
|
42191
43065
|
if (isReturnedByName || !isNodeOfType(child, "ReturnStatement") || !child.argument) return;
|
|
@@ -42195,7 +43069,7 @@ const doesEffectCallbackReturnName = (effectCallback, name) => {
|
|
|
42195
43069
|
return isReturnedByName;
|
|
42196
43070
|
};
|
|
42197
43071
|
const isFunctionReturnedFromEffectCallback = (functionNode, effectCallback) => {
|
|
42198
|
-
if (!isFunctionLike$
|
|
43072
|
+
if (!isFunctionLike$1(effectCallback)) return false;
|
|
42199
43073
|
if (isNodeOfType(functionNode.parent, "ReturnStatement")) return true;
|
|
42200
43074
|
if (effectCallback.body === functionNode) return true;
|
|
42201
43075
|
const cleanupBindingName = getFunctionBindingName$1(functionNode);
|
|
@@ -42225,7 +43099,7 @@ const isInsideEffectCleanupReturn = (node, ownerScope) => {
|
|
|
42225
43099
|
};
|
|
42226
43100
|
const hasRefCurrentReassignmentAfterClear = (clearCall, refName) => {
|
|
42227
43101
|
const enclosingFunction = findEnclosingFunction$1(clearCall);
|
|
42228
|
-
if (!isFunctionLike$
|
|
43102
|
+
if (!isFunctionLike$1(enclosingFunction)) return false;
|
|
42229
43103
|
if (!isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
|
|
42230
43104
|
const clearStart = getRangeStart(clearCall);
|
|
42231
43105
|
if (clearStart === null) return false;
|
|
@@ -43040,7 +43914,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
|
|
|
43040
43914
|
let currentNode = node;
|
|
43041
43915
|
let parentNode = currentNode.parent;
|
|
43042
43916
|
while (parentNode) {
|
|
43043
|
-
if (isFunctionLike$
|
|
43917
|
+
if (isFunctionLike$1(parentNode) && !executesDuringRender(parentNode, context.scopes)) break;
|
|
43044
43918
|
if (isNodeOfType(parentNode, "LogicalExpression") && (parentNode.operator === "&&" || parentNode.operator === "||") && parentNode.right === currentNode && readAvailabilityWhenPredicate(parentNode.left, browserGlobalName, context, parentNode.operator === "&&") === true) return true;
|
|
43045
43919
|
if (isNodeOfType(parentNode, "ConditionalExpression")) {
|
|
43046
43920
|
if (parentNode.consequent === currentNode && readAvailabilityWhenPredicate(parentNode.test, browserGlobalName, context, true) === true || parentNode.alternate === currentNode && readAvailabilityWhenPredicate(parentNode.test, browserGlobalName, context, false) === true) return true;
|
|
@@ -43055,7 +43929,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
|
|
|
43055
43929
|
};
|
|
43056
43930
|
const isAfterAvailabilityEarlyExit = (node, componentOrHookNode, browserGlobalName, context) => {
|
|
43057
43931
|
const enclosingFunction = findEnclosingFunction$1(node);
|
|
43058
|
-
if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$
|
|
43932
|
+
if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$1(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
|
|
43059
43933
|
let currentNode = node;
|
|
43060
43934
|
while (currentNode !== enclosingFunction) {
|
|
43061
43935
|
const parentNode = currentNode.parent;
|
|
@@ -44349,7 +45223,7 @@ const isReactClassComponent = (classNode, scopes) => {
|
|
|
44349
45223
|
const findEnclosingComponent = (node, scopes, controlFlow) => {
|
|
44350
45224
|
let walker = node.parent;
|
|
44351
45225
|
while (walker) {
|
|
44352
|
-
if (isFunctionLike$
|
|
45226
|
+
if (isFunctionLike$1(walker)) {
|
|
44353
45227
|
const componentName = inferFunctionLikeName(walker);
|
|
44354
45228
|
if (componentName && isReactComponentName(componentName) && functionContainsJsxOrCreateElement(walker, scopes, controlFlow)) return {
|
|
44355
45229
|
component: walker,
|
|
@@ -45694,7 +46568,7 @@ const isComponentLikeFunction = (functionNode) => {
|
|
|
45694
46568
|
const hasComponentLikeAncestorFunction = (node) => {
|
|
45695
46569
|
let cursor = node.parent;
|
|
45696
46570
|
while (cursor) {
|
|
45697
|
-
if (isFunctionLike$
|
|
46571
|
+
if (isFunctionLike$1(cursor) && isComponentLikeFunction(cursor)) return true;
|
|
45698
46572
|
cursor = cursor.parent ?? null;
|
|
45699
46573
|
}
|
|
45700
46574
|
return false;
|
|
@@ -45705,7 +46579,7 @@ const isDeclaringFunctionComponentLike = (declaringFunction) => {
|
|
|
45705
46579
|
return hasComponentLikeAncestorFunction(declaringFunction);
|
|
45706
46580
|
};
|
|
45707
46581
|
const destructuresChildrenAsFirstParam = (functionNode) => {
|
|
45708
|
-
if (!isFunctionLike$
|
|
46582
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
45709
46583
|
const firstParam = functionNode.params[0];
|
|
45710
46584
|
if (!firstParam || !isNodeOfType(firstParam, "ObjectPattern")) return false;
|
|
45711
46585
|
return firstParam.properties.some((property) => isNodeOfType(property, "Property") && isNodeOfType(property.key, "Identifier") && property.key.name === "children");
|
|
@@ -45720,7 +46594,7 @@ const findChildrenDestructuringFunction = (identifier, scopes) => {
|
|
|
45720
46594
|
}
|
|
45721
46595
|
let cursor = identifier.parent;
|
|
45722
46596
|
while (cursor) {
|
|
45723
|
-
if (isFunctionLike$
|
|
46597
|
+
if (isFunctionLike$1(cursor)) {
|
|
45724
46598
|
if (destructuresChildrenAsFirstParam(cursor)) return cursor;
|
|
45725
46599
|
return null;
|
|
45726
46600
|
}
|
|
@@ -47120,7 +47994,7 @@ const isListenerCollectionInitializer = (init) => {
|
|
|
47120
47994
|
return isNodeOfType(init, "NewExpression") && isNodeOfType(init.callee, "Identifier") && init.callee.name === "Set";
|
|
47121
47995
|
};
|
|
47122
47996
|
const functionRegistersParameterIntoCollection = (functionNode, listenerCollectionNames) => {
|
|
47123
|
-
if (!isFunctionLike$
|
|
47997
|
+
if (!isFunctionLike$1(functionNode)) return false;
|
|
47124
47998
|
const firstParam = functionNode.params?.[0];
|
|
47125
47999
|
if (!isNodeOfType(firstParam, "Identifier")) return false;
|
|
47126
48000
|
const listenerParamName = firstParam.name;
|
|
@@ -47156,7 +48030,7 @@ const buildModuleScopeStoreIndex = (programRoot) => {
|
|
|
47156
48030
|
for (const declarator of unwrapped.declarations ?? []) {
|
|
47157
48031
|
if (!isNodeOfType(declarator.id, "Identifier")) continue;
|
|
47158
48032
|
const init = declarator.init ?? null;
|
|
47159
|
-
if ((unwrapped.kind === "let" || unwrapped.kind === "var") && init && !isFunctionLike$
|
|
48033
|
+
if ((unwrapped.kind === "let" || unwrapped.kind === "var") && init && !isFunctionLike$1(init)) {
|
|
47160
48034
|
mutableBindingNames.add(declarator.id.name);
|
|
47161
48035
|
continue;
|
|
47162
48036
|
}
|
|
@@ -47164,7 +48038,7 @@ const buildModuleScopeStoreIndex = (programRoot) => {
|
|
|
47164
48038
|
listenerCollectionNames.add(declarator.id.name);
|
|
47165
48039
|
continue;
|
|
47166
48040
|
}
|
|
47167
|
-
if (init && isFunctionLike$
|
|
48041
|
+
if (init && isFunctionLike$1(init)) moduleFunctionsByName.set(declarator.id.name, init);
|
|
47168
48042
|
}
|
|
47169
48043
|
}
|
|
47170
48044
|
const subscribeFunctionNames = /* @__PURE__ */ new Set();
|
|
@@ -47177,7 +48051,7 @@ const buildModuleScopeStoreIndex = (programRoot) => {
|
|
|
47177
48051
|
const getModuleStoreSnapshotName = (useStateCall, storeIndex) => {
|
|
47178
48052
|
if (!isNodeOfType(useStateCall, "CallExpression")) return null;
|
|
47179
48053
|
let initialArgument = stripParenExpression(useStateCall.arguments?.[0]);
|
|
47180
|
-
if (initialArgument && isFunctionLike$
|
|
48054
|
+
if (initialArgument && isFunctionLike$1(initialArgument) && !isNodeOfType(initialArgument.body, "BlockStatement")) initialArgument = stripParenExpression(initialArgument.body);
|
|
47181
48055
|
if (!isNodeOfType(initialArgument, "Identifier")) return null;
|
|
47182
48056
|
if (!storeIndex.mutableBindingNames.has(initialArgument.name)) return null;
|
|
47183
48057
|
const binding = findVariableInitializer(initialArgument, initialArgument.name);
|
|
@@ -47188,7 +48062,7 @@ const argumentForwardsSetter = (argument, setterName) => {
|
|
|
47188
48062
|
if (!argument) return false;
|
|
47189
48063
|
const unwrapped = stripParenExpression(argument);
|
|
47190
48064
|
if (isNodeOfType(unwrapped, "Identifier")) return unwrapped.name === setterName;
|
|
47191
|
-
if (!isFunctionLike$
|
|
48065
|
+
if (!isFunctionLike$1(unwrapped)) return false;
|
|
47192
48066
|
let callsSetter = false;
|
|
47193
48067
|
walkAst(unwrapped.body, (child) => {
|
|
47194
48068
|
if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && child.callee.name === setterName) {
|
|
@@ -47200,7 +48074,7 @@ const argumentForwardsSetter = (argument, setterName) => {
|
|
|
47200
48074
|
};
|
|
47201
48075
|
const findModuleSubscribeCallForwardingSetter = (effectCallback, setterName, storeIndex) => {
|
|
47202
48076
|
let matchedCall = null;
|
|
47203
|
-
walkAst((isFunctionLike$
|
|
48077
|
+
walkAst((isFunctionLike$1(effectCallback) ? effectCallback.body : null) ?? effectCallback, (child) => {
|
|
47204
48078
|
if (matchedCall) return false;
|
|
47205
48079
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
47206
48080
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
@@ -47304,7 +48178,7 @@ const preferUseSyncExternalStore = defineRule({
|
|
|
47304
48178
|
if (!isNodeOfType(depsNode, "ArrayExpression")) continue;
|
|
47305
48179
|
if ((depsNode.elements?.length ?? 0) !== 0) continue;
|
|
47306
48180
|
const callback = getEffectCallback(effectCall);
|
|
47307
|
-
if (!callback || !isFunctionLike$
|
|
48181
|
+
if (!callback || !isFunctionLike$1(callback)) continue;
|
|
47308
48182
|
for (const { binding, storeName } of snapshotBindings) {
|
|
47309
48183
|
if (reportedDeclarators.has(binding.declarator)) continue;
|
|
47310
48184
|
if (!findModuleSubscribeCallForwardingSetter(callback, binding.setterName, storeIndex)) continue;
|
|
@@ -47372,7 +48246,7 @@ const findLargestCoUpdatedSetterGroup = (componentBody, setterNames, initializer
|
|
|
47372
48246
|
}
|
|
47373
48247
|
if (hasDataCarryingWrite) largestGroupSize = Math.max(largestGroupSize, groupSetterNames.size);
|
|
47374
48248
|
}
|
|
47375
|
-
const enteringNestedFunction = isInsideNestedFunction || isFunctionLike$
|
|
48249
|
+
const enteringNestedFunction = isInsideNestedFunction || isFunctionLike$1(node);
|
|
47376
48250
|
const record = node;
|
|
47377
48251
|
for (const key of Object.keys(record)) {
|
|
47378
48252
|
if (key === "parent" || key === "type") continue;
|
|
@@ -47827,21 +48701,21 @@ const isTanstackQueryResult = (expression, context) => Boolean(resolveTanstackQu
|
|
|
47827
48701
|
const isStaticRefetchMember = (memberExpression) => getStaticPropertyKeyName(memberExpression, { allowComputedString: true }) === "refetch";
|
|
47828
48702
|
const resolveCalledFunction = (callee, context) => {
|
|
47829
48703
|
const unwrappedCallee = stripParenExpression(callee);
|
|
47830
|
-
if (isFunctionLike$
|
|
48704
|
+
if (isFunctionLike$1(unwrappedCallee)) return unwrappedCallee;
|
|
47831
48705
|
if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
|
|
47832
48706
|
const symbol = resolveConstIdentifierAlias(unwrappedCallee, context.scopes);
|
|
47833
48707
|
if (!symbol) return null;
|
|
47834
48708
|
const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
|
|
47835
|
-
return candidate && isFunctionLike$
|
|
48709
|
+
return candidate && isFunctionLike$1(candidate) ? candidate : null;
|
|
47836
48710
|
};
|
|
47837
48711
|
const hasSuspensionBefore = (functionNode, boundary, context) => {
|
|
47838
|
-
if (!isFunctionLike$
|
|
48712
|
+
if (!isFunctionLike$1(functionNode)) return true;
|
|
47839
48713
|
if (functionNode.generator) return true;
|
|
47840
48714
|
const boundaryStart = getRangeStart(boundary);
|
|
47841
48715
|
if (boundaryStart === null) return true;
|
|
47842
48716
|
let hasSuspension = false;
|
|
47843
48717
|
walkAst(functionNode, (node) => {
|
|
47844
|
-
if (node !== functionNode && isFunctionLike$
|
|
48718
|
+
if (node !== functionNode && isFunctionLike$1(node)) return false;
|
|
47845
48719
|
if (!isNodeOfType(node, "AwaitExpression") || !isNodeReachableWithinFunction(node, context)) return;
|
|
47846
48720
|
const suspensionStart = getRangeStart(node);
|
|
47847
48721
|
if (suspensionStart !== null && suspensionStart < boundaryStart) {
|
|
@@ -47865,7 +48739,7 @@ const functionInvokesTarget = (callerFunction, targetFunction, context, visitedF
|
|
|
47865
48739
|
visitedFunctions.add(callerFunction);
|
|
47866
48740
|
let invokesTarget = false;
|
|
47867
48741
|
walkAst(callerFunction, (node) => {
|
|
47868
|
-
if (node !== callerFunction && isFunctionLike$
|
|
48742
|
+
if (node !== callerFunction && isFunctionLike$1(node)) return false;
|
|
47869
48743
|
if (!isNodeOfType(node, "CallExpression")) return;
|
|
47870
48744
|
if (!isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context) || !canCrossSuspension && hasSuspensionBefore(callerFunction, node, context)) return;
|
|
47871
48745
|
const calledFunction = resolveCalledFunction(node.callee, context);
|
|
@@ -47882,7 +48756,7 @@ const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
|
|
|
47882
48756
|
if (!boundaryFunction || boundaryStart === null) return false;
|
|
47883
48757
|
let isInvokedBefore = false;
|
|
47884
48758
|
walkAst(boundaryFunction, (node) => {
|
|
47885
|
-
if (node !== boundaryFunction && isFunctionLike$
|
|
48759
|
+
if (node !== boundaryFunction && isFunctionLike$1(node)) return false;
|
|
47886
48760
|
if (!isNodeOfType(node, "CallExpression")) return;
|
|
47887
48761
|
const callStart = getRangeStart(node);
|
|
47888
48762
|
if (callStart === null || callStart >= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, boundaryFunction, context) || hasSuspensionBefore(boundaryFunction, node, context)) return;
|
|
@@ -47899,7 +48773,7 @@ const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, conte
|
|
|
47899
48773
|
if (boundaryStart === null) return false;
|
|
47900
48774
|
let isInvokedAfter = false;
|
|
47901
48775
|
walkAst(callerFunction, (node) => {
|
|
47902
|
-
if (node !== callerFunction && isFunctionLike$
|
|
48776
|
+
if (node !== callerFunction && isFunctionLike$1(node)) return false;
|
|
47903
48777
|
if (!isNodeOfType(node, "CallExpression")) return;
|
|
47904
48778
|
const callStart = getRangeStart(node);
|
|
47905
48779
|
if (callStart === null || callStart <= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context)) return;
|
|
@@ -48033,7 +48907,7 @@ const queryNoQueryInEffect = defineRule({
|
|
|
48033
48907
|
if (!callback) return;
|
|
48034
48908
|
const effectInvokedFunctions = collectEffectInvokedFunctions(callback);
|
|
48035
48909
|
walkAst(callback, (child) => {
|
|
48036
|
-
if (child !== callback && isFunctionLike$
|
|
48910
|
+
if (child !== callback && isFunctionLike$1(child) && !effectInvokedFunctions.has(child)) return false;
|
|
48037
48911
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
48038
48912
|
if (isTanstackRefetchCall(child, context, callback)) context.report({
|
|
48039
48913
|
node: child,
|
|
@@ -48623,7 +49497,7 @@ const reduxUseselectorInlineDerivation = defineRule({
|
|
|
48623
49497
|
if (!body) return;
|
|
48624
49498
|
const returnedExpressions = [];
|
|
48625
49499
|
if (isNodeOfType(body, "BlockStatement")) walkAst(body, (node) => {
|
|
48626
|
-
if (node !== body && isFunctionLike$
|
|
49500
|
+
if (node !== body && isFunctionLike$1(node)) return false;
|
|
48627
49501
|
if (isNodeOfType(node, "ReturnStatement")) {
|
|
48628
49502
|
if (node.argument) returnedExpressions.push(node.argument);
|
|
48629
49503
|
return false;
|
|
@@ -48915,7 +49789,7 @@ const renderingHydrationMismatchTime = defineRule({
|
|
|
48915
49789
|
return;
|
|
48916
49790
|
}
|
|
48917
49791
|
walkAst(node.expression, (child) => {
|
|
48918
|
-
if (isFunctionLike$
|
|
49792
|
+
if (isFunctionLike$1(child) && !executesDuringRender(child, context.scopes)) return false;
|
|
48919
49793
|
for (const pattern of NONDETERMINISTIC_RENDER_PATTERNS) if (pattern.matches(child)) {
|
|
48920
49794
|
if (hasSuppressHydrationWarningAttribute(findOpeningElementOfChild(node))) return;
|
|
48921
49795
|
if (isGatedByFalsyInitialState(child, context.scopes)) return;
|
|
@@ -49186,7 +50060,7 @@ const hasOwnAwait = (functionBody) => {
|
|
|
49186
50060
|
let found = false;
|
|
49187
50061
|
walkAst(functionBody, (child) => {
|
|
49188
50062
|
if (found) return;
|
|
49189
|
-
if (child !== functionBody && isFunctionLike$
|
|
50063
|
+
if (child !== functionBody && isFunctionLike$1(child)) return false;
|
|
49190
50064
|
if (isNodeOfType(child, "AwaitExpression")) found = true;
|
|
49191
50065
|
});
|
|
49192
50066
|
return found;
|
|
@@ -49236,7 +50110,7 @@ const hasAsyncLoadingWork = (fnBody, setterName) => {
|
|
|
49236
50110
|
return false;
|
|
49237
50111
|
}
|
|
49238
50112
|
if (setterName !== null && PROMISE_CHAIN_METHOD_NAMES.has(callee.property.name)) for (const argument of child.arguments ?? []) {
|
|
49239
|
-
if (!isFunctionLike$
|
|
50113
|
+
if (!isFunctionLike$1(argument)) continue;
|
|
49240
50114
|
if (callsIdentifier(argument.body, setterName)) {
|
|
49241
50115
|
found = true;
|
|
49242
50116
|
return false;
|
|
@@ -49245,7 +50119,7 @@ const hasAsyncLoadingWork = (fnBody, setterName) => {
|
|
|
49245
50119
|
}
|
|
49246
50120
|
return;
|
|
49247
50121
|
}
|
|
49248
|
-
if (setterName !== null && isFunctionLike$
|
|
50122
|
+
if (setterName !== null && isFunctionLike$1(child)) {
|
|
49249
50123
|
const functionBody = child.body;
|
|
49250
50124
|
if ((Boolean(child.async) || hasOwnAwait(functionBody)) && callsIdentifier(functionBody, setterName)) {
|
|
49251
50125
|
found = true;
|
|
@@ -49310,7 +50184,7 @@ const collectAsyncSignalNames = (fnBody) => {
|
|
|
49310
50184
|
if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier")) return;
|
|
49311
50185
|
const init = child.init;
|
|
49312
50186
|
if (!init) return;
|
|
49313
|
-
if (isFunctionLike$
|
|
50187
|
+
if (isFunctionLike$1(init) && Boolean(init.async)) {
|
|
49314
50188
|
names.add(child.id.name);
|
|
49315
50189
|
return;
|
|
49316
50190
|
}
|
|
@@ -49333,7 +50207,7 @@ const setterCalledAlongsideAsyncSignal = (fnBody, setterName) => {
|
|
|
49333
50207
|
let found = false;
|
|
49334
50208
|
walkAst(fnBody, (child) => {
|
|
49335
50209
|
if (found) return false;
|
|
49336
|
-
if (!isFunctionLike$
|
|
50210
|
+
if (!isFunctionLike$1(child)) return;
|
|
49337
50211
|
const functionBody = child.body;
|
|
49338
50212
|
if (callsIdentifier(functionBody, setterName) && callsAnyIdentifier(functionBody, asyncSignalNames)) {
|
|
49339
50213
|
found = true;
|
|
@@ -49350,7 +50224,7 @@ const setterCalledInEventListenerHandler = (fnBody, setterName) => {
|
|
|
49350
50224
|
if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || !isNodeOfType(child.callee.property, "Identifier") || child.callee.property.name !== "addEventListener") return;
|
|
49351
50225
|
const handler = child.arguments?.[1];
|
|
49352
50226
|
if (!handler) return;
|
|
49353
|
-
if (isFunctionLike$
|
|
50227
|
+
if (isFunctionLike$1(handler)) {
|
|
49354
50228
|
if (callsIdentifier(handler.body, setterName)) foundInline = true;
|
|
49355
50229
|
return;
|
|
49356
50230
|
}
|
|
@@ -49361,7 +50235,7 @@ const setterCalledInEventListenerHandler = (fnBody, setterName) => {
|
|
|
49361
50235
|
let foundNamed = false;
|
|
49362
50236
|
walkAst(fnBody, (child) => {
|
|
49363
50237
|
if (foundNamed) return false;
|
|
49364
|
-
if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && handlerNames.has(child.id.name) && child.init && isFunctionLike$
|
|
50238
|
+
if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && handlerNames.has(child.id.name) && child.init && isFunctionLike$1(child.init) && callsIdentifier(child.init.body, setterName)) {
|
|
49365
50239
|
foundNamed = true;
|
|
49366
50240
|
return false;
|
|
49367
50241
|
}
|
|
@@ -50183,6 +51057,357 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
|
|
|
50183
51057
|
}
|
|
50184
51058
|
});
|
|
50185
51059
|
//#endregion
|
|
51060
|
+
//#region src/plugin/utils/build-same-file-memo-comparator-registry.ts
|
|
51061
|
+
const unwrapTopLevelDeclaration = (statement) => {
|
|
51062
|
+
if (isNodeOfType(statement, "ExportNamedDeclaration")) return statement.declaration;
|
|
51063
|
+
return statement;
|
|
51064
|
+
};
|
|
51065
|
+
const buildSameFileMemoComparatorRegistry = (program) => {
|
|
51066
|
+
const registry = /* @__PURE__ */ new Map();
|
|
51067
|
+
if (!isNodeOfType(program, "Program")) return registry;
|
|
51068
|
+
const memoBindings = /* @__PURE__ */ new Set();
|
|
51069
|
+
const reactNamespaceBindings = /* @__PURE__ */ new Set();
|
|
51070
|
+
for (const statement of program.body) {
|
|
51071
|
+
if (!isNodeOfType(statement, "ImportDeclaration") || statement.source.value !== "react") continue;
|
|
51072
|
+
for (const specifier of statement.specifiers) if (isNodeOfType(specifier, "ImportDefaultSpecifier") || isNodeOfType(specifier, "ImportNamespaceSpecifier")) reactNamespaceBindings.add(specifier.local.name);
|
|
51073
|
+
else if (isNodeOfType(specifier, "ImportSpecifier") && getImportedName(specifier) === "memo") memoBindings.add(specifier.local.name);
|
|
51074
|
+
}
|
|
51075
|
+
for (const statement of program.body) {
|
|
51076
|
+
const declaration = unwrapTopLevelDeclaration(statement);
|
|
51077
|
+
if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) continue;
|
|
51078
|
+
for (const declarator of declaration.declarations ?? []) {
|
|
51079
|
+
if (!isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.id, "Identifier") || !declarator.init) continue;
|
|
51080
|
+
const initializer = stripParenExpression(declarator.init);
|
|
51081
|
+
if (!isNodeOfType(initializer, "CallExpression")) continue;
|
|
51082
|
+
const callee = stripParenExpression(initializer.callee);
|
|
51083
|
+
const isNamedMemo = isNodeOfType(callee, "Identifier") && memoBindings.has(callee.name);
|
|
51084
|
+
const isNamespaceMemo = isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.object, "Identifier") && reactNamespaceBindings.has(callee.object.name) && isNodeOfType(callee.property, "Identifier") && callee.property.name === "memo";
|
|
51085
|
+
if (!isNamedMemo && !isNamespaceMemo) continue;
|
|
51086
|
+
const comparator = initializer.arguments?.[1];
|
|
51087
|
+
if (!comparator || isNodeOfType(comparator, "SpreadElement")) continue;
|
|
51088
|
+
registry.set(declarator.id.name, {
|
|
51089
|
+
bindingIdentifier: declarator.id,
|
|
51090
|
+
comparator
|
|
51091
|
+
});
|
|
51092
|
+
}
|
|
51093
|
+
}
|
|
51094
|
+
return registry;
|
|
51095
|
+
};
|
|
51096
|
+
//#endregion
|
|
51097
|
+
//#region src/plugin/utils/comparator-proves-empty-prop-equality.ts
|
|
51098
|
+
const UNKNOWN_VALUE = { kind: "unknown" };
|
|
51099
|
+
const TRUE_VALUE = {
|
|
51100
|
+
kind: "boolean",
|
|
51101
|
+
value: true
|
|
51102
|
+
};
|
|
51103
|
+
const FALSE_VALUE = {
|
|
51104
|
+
kind: "boolean",
|
|
51105
|
+
value: false
|
|
51106
|
+
};
|
|
51107
|
+
const UNDEFINED_VALUE = { kind: "undefined" };
|
|
51108
|
+
const PREVIOUS_PROPS_VALUE = { kind: "previous-props" };
|
|
51109
|
+
const NEXT_PROPS_VALUE = { kind: "next-props" };
|
|
51110
|
+
const OBJECT_PROTOTYPE_PROPERTY_NAMES = new Set([
|
|
51111
|
+
"__defineGetter__",
|
|
51112
|
+
"__defineSetter__",
|
|
51113
|
+
"__lookupGetter__",
|
|
51114
|
+
"__lookupSetter__",
|
|
51115
|
+
"__proto__",
|
|
51116
|
+
"constructor",
|
|
51117
|
+
"hasOwnProperty",
|
|
51118
|
+
"isPrototypeOf",
|
|
51119
|
+
"propertyIsEnumerable",
|
|
51120
|
+
"toLocaleString",
|
|
51121
|
+
"toString",
|
|
51122
|
+
"valueOf"
|
|
51123
|
+
]);
|
|
51124
|
+
const PROVABLY_CALLABLE_GLOBAL_NAMES = new Set([
|
|
51125
|
+
"Boolean",
|
|
51126
|
+
"Number",
|
|
51127
|
+
"String"
|
|
51128
|
+
]);
|
|
51129
|
+
const booleanValue = (value) => value ? TRUE_VALUE : FALSE_VALUE;
|
|
51130
|
+
const constantBooleanFormula = (value) => ({
|
|
51131
|
+
kind: "constant",
|
|
51132
|
+
value
|
|
51133
|
+
});
|
|
51134
|
+
const getBooleanFormula = (value) => {
|
|
51135
|
+
if (value.kind === "boolean") return constantBooleanFormula(value.value === true);
|
|
51136
|
+
return value.kind === "boolean-formula" && value.formula ? value.formula : null;
|
|
51137
|
+
};
|
|
51138
|
+
const booleanFormulaValue = (formula) => formula.kind === "constant" ? booleanValue(formula.value === true) : {
|
|
51139
|
+
kind: "boolean-formula",
|
|
51140
|
+
formula
|
|
51141
|
+
};
|
|
51142
|
+
const negateBooleanFormula = (formula) => {
|
|
51143
|
+
if (formula.kind === "constant") return constantBooleanFormula(formula.value !== true);
|
|
51144
|
+
if (formula.kind === "not" && formula.operand) return formula.operand;
|
|
51145
|
+
return {
|
|
51146
|
+
kind: "not",
|
|
51147
|
+
operand: formula
|
|
51148
|
+
};
|
|
51149
|
+
};
|
|
51150
|
+
const combineBooleanFormulas = (operator, left, right) => {
|
|
51151
|
+
if (left.kind === "constant") {
|
|
51152
|
+
if (operator === "and") return left.value === true ? right : left;
|
|
51153
|
+
return left.value === true ? left : right;
|
|
51154
|
+
}
|
|
51155
|
+
if (right.kind === "constant") {
|
|
51156
|
+
if (operator === "and") return right.value === true ? left : right;
|
|
51157
|
+
return right.value === true ? right : left;
|
|
51158
|
+
}
|
|
51159
|
+
return {
|
|
51160
|
+
kind: operator,
|
|
51161
|
+
left,
|
|
51162
|
+
right
|
|
51163
|
+
};
|
|
51164
|
+
};
|
|
51165
|
+
const conditionalBooleanFormula = (test, consequent, alternate) => {
|
|
51166
|
+
if (test.kind === "constant") return test.value === true ? consequent : alternate;
|
|
51167
|
+
return {
|
|
51168
|
+
kind: "conditional",
|
|
51169
|
+
test,
|
|
51170
|
+
consequent,
|
|
51171
|
+
alternate
|
|
51172
|
+
};
|
|
51173
|
+
};
|
|
51174
|
+
const collectBooleanFormulaAtomKeys = (formula, atomKeys) => {
|
|
51175
|
+
if (formula.kind === "atom") {
|
|
51176
|
+
if (formula.atomKey) atomKeys.add(formula.atomKey);
|
|
51177
|
+
return;
|
|
51178
|
+
}
|
|
51179
|
+
if (formula.operand) collectBooleanFormulaAtomKeys(formula.operand, atomKeys);
|
|
51180
|
+
if (formula.left) collectBooleanFormulaAtomKeys(formula.left, atomKeys);
|
|
51181
|
+
if (formula.right) collectBooleanFormulaAtomKeys(formula.right, atomKeys);
|
|
51182
|
+
if (formula.test) collectBooleanFormulaAtomKeys(formula.test, atomKeys);
|
|
51183
|
+
if (formula.consequent) collectBooleanFormulaAtomKeys(formula.consequent, atomKeys);
|
|
51184
|
+
if (formula.alternate) collectBooleanFormulaAtomKeys(formula.alternate, atomKeys);
|
|
51185
|
+
};
|
|
51186
|
+
const evaluateBooleanFormula = (formula, atomValues) => {
|
|
51187
|
+
if (formula.kind === "constant") return formula.value ?? null;
|
|
51188
|
+
if (formula.kind === "atom") return formula.atomKey ? atomValues.get(formula.atomKey) ?? null : null;
|
|
51189
|
+
if (formula.kind === "not") {
|
|
51190
|
+
const operand = formula.operand ? evaluateBooleanFormula(formula.operand, atomValues) : null;
|
|
51191
|
+
return operand === null ? null : !operand;
|
|
51192
|
+
}
|
|
51193
|
+
if (formula.kind === "and" || formula.kind === "or") {
|
|
51194
|
+
const left = formula.left ? evaluateBooleanFormula(formula.left, atomValues) : null;
|
|
51195
|
+
const right = formula.right ? evaluateBooleanFormula(formula.right, atomValues) : null;
|
|
51196
|
+
if (left === null || right === null) return null;
|
|
51197
|
+
return formula.kind === "and" ? left && right : left || right;
|
|
51198
|
+
}
|
|
51199
|
+
const test = formula.test ? evaluateBooleanFormula(formula.test, atomValues) : null;
|
|
51200
|
+
if (test === null) return null;
|
|
51201
|
+
const branch = test ? formula.consequent : formula.alternate;
|
|
51202
|
+
return branch ? evaluateBooleanFormula(branch, atomValues) : null;
|
|
51203
|
+
};
|
|
51204
|
+
const couldStableTargetReferencePreventRender = (distinctReferenceFormula, sharedReferenceFormula) => {
|
|
51205
|
+
const atomKeys = /* @__PURE__ */ new Set();
|
|
51206
|
+
collectBooleanFormulaAtomKeys(distinctReferenceFormula, atomKeys);
|
|
51207
|
+
collectBooleanFormulaAtomKeys(sharedReferenceFormula, atomKeys);
|
|
51208
|
+
if (atomKeys.size > 8) return true;
|
|
51209
|
+
const orderedAtomKeys = [...atomKeys];
|
|
51210
|
+
const assignmentCount = 2 ** orderedAtomKeys.length;
|
|
51211
|
+
for (let assignmentIndex = 0; assignmentIndex < assignmentCount; assignmentIndex += 1) {
|
|
51212
|
+
const atomValues = /* @__PURE__ */ new Map();
|
|
51213
|
+
for (const [atomIndex, atomKey] of orderedAtomKeys.entries()) atomValues.set(atomKey, Boolean(assignmentIndex & 1 << atomIndex));
|
|
51214
|
+
const distinctReferenceResult = evaluateBooleanFormula(distinctReferenceFormula, atomValues);
|
|
51215
|
+
const sharedReferenceResult = evaluateBooleanFormula(sharedReferenceFormula, atomValues);
|
|
51216
|
+
if (distinctReferenceResult === null || sharedReferenceResult === null) return true;
|
|
51217
|
+
if (!distinctReferenceResult && sharedReferenceResult) return true;
|
|
51218
|
+
}
|
|
51219
|
+
return false;
|
|
51220
|
+
};
|
|
51221
|
+
const emptyReferenceValue = (kind, referenceOrigin) => ({
|
|
51222
|
+
kind,
|
|
51223
|
+
referenceOrigin
|
|
51224
|
+
});
|
|
51225
|
+
const evaluateEquality = (left, right, emptyReferencesAreEqual, equalityKind) => {
|
|
51226
|
+
if (left.kind === "unknown" || right.kind === "unknown") return UNKNOWN_VALUE;
|
|
51227
|
+
const leftBooleanFormula = getBooleanFormula(left);
|
|
51228
|
+
const rightBooleanFormula = getBooleanFormula(right);
|
|
51229
|
+
if (leftBooleanFormula || rightBooleanFormula) {
|
|
51230
|
+
if (!leftBooleanFormula || !rightBooleanFormula) return UNKNOWN_VALUE;
|
|
51231
|
+
return booleanFormulaValue(combineBooleanFormulas("or", combineBooleanFormulas("and", leftBooleanFormula, rightBooleanFormula), combineBooleanFormulas("and", negateBooleanFormula(leftBooleanFormula), negateBooleanFormula(rightBooleanFormula))));
|
|
51232
|
+
}
|
|
51233
|
+
if (left.kind === "prop-symbol" || right.kind === "prop-symbol") {
|
|
51234
|
+
if (left.kind !== "prop-symbol" || right.kind !== "prop-symbol") return UNKNOWN_VALUE;
|
|
51235
|
+
if (left.propOwner === right.propOwner && left.value === right.value) return TRUE_VALUE;
|
|
51236
|
+
if (left.propOwner === right.propOwner || left.value !== right.value) return UNKNOWN_VALUE;
|
|
51237
|
+
return booleanFormulaValue({
|
|
51238
|
+
kind: "atom",
|
|
51239
|
+
atomKey: `${equalityKind}:${String(left.value)}`
|
|
51240
|
+
});
|
|
51241
|
+
}
|
|
51242
|
+
if (left.kind === "empty-array" || left.kind === "empty-object") {
|
|
51243
|
+
if (left.kind !== right.kind) return FALSE_VALUE;
|
|
51244
|
+
if (left.referenceOrigin === right.referenceOrigin) return TRUE_VALUE;
|
|
51245
|
+
return booleanValue((left.referenceOrigin === "previous-target" && right.referenceOrigin === "next-target" || left.referenceOrigin === "next-target" && right.referenceOrigin === "previous-target") && emptyReferencesAreEqual);
|
|
51246
|
+
}
|
|
51247
|
+
if (right.kind === "empty-array" || right.kind === "empty-object") return FALSE_VALUE;
|
|
51248
|
+
if (left.kind !== right.kind) return FALSE_VALUE;
|
|
51249
|
+
if (left.kind === "previous-props" || left.kind === "next-props") return UNKNOWN_VALUE;
|
|
51250
|
+
if (left.kind === "symbol" && left.value !== right.value) return UNKNOWN_VALUE;
|
|
51251
|
+
return booleanValue(left.value === right.value);
|
|
51252
|
+
};
|
|
51253
|
+
const getReturnedExpression = (functionNode) => {
|
|
51254
|
+
if (!isFunctionLike$1(functionNode)) return null;
|
|
51255
|
+
if (isNodeOfType(functionNode, "ArrowFunctionExpression")) {
|
|
51256
|
+
const body = functionNode.body;
|
|
51257
|
+
if (!isNodeOfType(body, "BlockStatement")) return body;
|
|
51258
|
+
}
|
|
51259
|
+
const body = functionNode.body;
|
|
51260
|
+
if (!isNodeOfType(body, "BlockStatement") || body.body.length !== 1) return null;
|
|
51261
|
+
const returnStatement = body.body[0];
|
|
51262
|
+
return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument ? returnStatement.argument : null;
|
|
51263
|
+
};
|
|
51264
|
+
const isProvablyCallable = (expression, state) => {
|
|
51265
|
+
const node = stripParenExpression(expression);
|
|
51266
|
+
if (isFunctionLike$1(node)) return true;
|
|
51267
|
+
if (isNodeOfType(node, "Identifier") && PROVABLY_CALLABLE_GLOBAL_NAMES.has(node.name) && state.scopes.isGlobalReference(node)) return true;
|
|
51268
|
+
return isFunctionLike$1(resolveExactLocalFunction(node, state.scopes));
|
|
51269
|
+
};
|
|
51270
|
+
const evaluateExpression = (expression, state) => {
|
|
51271
|
+
const node = stripParenExpression(expression);
|
|
51272
|
+
if (isNodeOfType(node, "Literal")) {
|
|
51273
|
+
if (typeof node.value === "boolean") return booleanValue(node.value);
|
|
51274
|
+
if (typeof node.value === "number") return {
|
|
51275
|
+
kind: "number",
|
|
51276
|
+
value: node.value
|
|
51277
|
+
};
|
|
51278
|
+
if (typeof node.value === "string") return {
|
|
51279
|
+
kind: "string",
|
|
51280
|
+
value: node.value
|
|
51281
|
+
};
|
|
51282
|
+
return node.value === void 0 ? UNDEFINED_VALUE : UNKNOWN_VALUE;
|
|
51283
|
+
}
|
|
51284
|
+
if (isNodeOfType(node, "Identifier")) {
|
|
51285
|
+
if (node.name === "undefined" && state.scopes.isGlobalReference(node)) return UNDEFINED_VALUE;
|
|
51286
|
+
return state.bindings.get(node.name) ?? UNKNOWN_VALUE;
|
|
51287
|
+
}
|
|
51288
|
+
if (isNodeOfType(node, "MemberExpression")) {
|
|
51289
|
+
const objectValue = evaluateExpression(node.object, state);
|
|
51290
|
+
if ((objectValue.kind === "empty-array" || objectValue.kind === "empty-object") && node.computed && isNodeOfType(node.property, "Literal") && (typeof node.property.value === "number" || typeof node.property.value === "string" && /^\d+$/.test(node.property.value))) return UNDEFINED_VALUE;
|
|
51291
|
+
const propertyName = getStaticPropertyName(node);
|
|
51292
|
+
if (propertyName === null) return UNKNOWN_VALUE;
|
|
51293
|
+
if (objectValue.kind === "previous-props" || objectValue.kind === "next-props") {
|
|
51294
|
+
if (propertyName === state.propName) {
|
|
51295
|
+
const referenceOrigin = objectValue.kind === "previous-props" ? "previous-target" : "next-target";
|
|
51296
|
+
return emptyReferenceValue(state.emptyLiteralKind === "array" ? "empty-array" : "empty-object", referenceOrigin);
|
|
51297
|
+
}
|
|
51298
|
+
return {
|
|
51299
|
+
kind: "prop-symbol",
|
|
51300
|
+
propOwner: objectValue.kind === "previous-props" ? "previous" : "next",
|
|
51301
|
+
value: propertyName
|
|
51302
|
+
};
|
|
51303
|
+
}
|
|
51304
|
+
if (objectValue.kind === "empty-array" && propertyName === "length") return {
|
|
51305
|
+
kind: "number",
|
|
51306
|
+
value: 0
|
|
51307
|
+
};
|
|
51308
|
+
if (objectValue.kind === "empty-object" && !OBJECT_PROTOTYPE_PROPERTY_NAMES.has(propertyName)) return UNDEFINED_VALUE;
|
|
51309
|
+
return UNKNOWN_VALUE;
|
|
51310
|
+
}
|
|
51311
|
+
if (isNodeOfType(node, "UnaryExpression") && node.operator === "!") {
|
|
51312
|
+
const argumentFormula = getBooleanFormula(evaluateExpression(node.argument, state));
|
|
51313
|
+
return argumentFormula ? booleanFormulaValue(negateBooleanFormula(argumentFormula)) : UNKNOWN_VALUE;
|
|
51314
|
+
}
|
|
51315
|
+
if (isNodeOfType(node, "LogicalExpression")) {
|
|
51316
|
+
const leftFormula = getBooleanFormula(evaluateExpression(node.left, state));
|
|
51317
|
+
if (!leftFormula) return UNKNOWN_VALUE;
|
|
51318
|
+
if (node.operator === "&&") {
|
|
51319
|
+
if (leftFormula.kind === "constant" && leftFormula.value === false) return FALSE_VALUE;
|
|
51320
|
+
const rightFormula = getBooleanFormula(evaluateExpression(node.right, state));
|
|
51321
|
+
return rightFormula ? booleanFormulaValue(combineBooleanFormulas("and", leftFormula, rightFormula)) : UNKNOWN_VALUE;
|
|
51322
|
+
}
|
|
51323
|
+
if (node.operator === "||") {
|
|
51324
|
+
if (leftFormula.kind === "constant" && leftFormula.value === true) return TRUE_VALUE;
|
|
51325
|
+
const rightFormula = getBooleanFormula(evaluateExpression(node.right, state));
|
|
51326
|
+
return rightFormula ? booleanFormulaValue(combineBooleanFormulas("or", leftFormula, rightFormula)) : UNKNOWN_VALUE;
|
|
51327
|
+
}
|
|
51328
|
+
return UNKNOWN_VALUE;
|
|
51329
|
+
}
|
|
51330
|
+
if (isNodeOfType(node, "BinaryExpression")) {
|
|
51331
|
+
const left = evaluateExpression(node.left, state);
|
|
51332
|
+
const right = evaluateExpression(node.right, state);
|
|
51333
|
+
if ([
|
|
51334
|
+
"===",
|
|
51335
|
+
"==",
|
|
51336
|
+
"!==",
|
|
51337
|
+
"!="
|
|
51338
|
+
].includes(node.operator)) {
|
|
51339
|
+
const equality = evaluateEquality(left, right, state.emptyReferencesAreEqual, node.operator === "===" || node.operator === "!==" ? "strict" : "loose");
|
|
51340
|
+
if (node.operator === "===" || node.operator === "==") return equality;
|
|
51341
|
+
const equalityFormula = getBooleanFormula(equality);
|
|
51342
|
+
return equalityFormula ? booleanFormulaValue(negateBooleanFormula(equalityFormula)) : UNKNOWN_VALUE;
|
|
51343
|
+
}
|
|
51344
|
+
if (left.kind !== "number" || right.kind !== "number") return UNKNOWN_VALUE;
|
|
51345
|
+
if (node.operator === "<") return booleanValue(Number(left.value) < Number(right.value));
|
|
51346
|
+
if (node.operator === "<=") return booleanValue(Number(left.value) <= Number(right.value));
|
|
51347
|
+
if (node.operator === ">") return booleanValue(Number(left.value) > Number(right.value));
|
|
51348
|
+
if (node.operator === ">=") return booleanValue(Number(left.value) >= Number(right.value));
|
|
51349
|
+
return UNKNOWN_VALUE;
|
|
51350
|
+
}
|
|
51351
|
+
if (isNodeOfType(node, "ConditionalExpression")) {
|
|
51352
|
+
const testFormula = getBooleanFormula(evaluateExpression(node.test, state));
|
|
51353
|
+
if (!testFormula) return UNKNOWN_VALUE;
|
|
51354
|
+
if (testFormula.kind === "constant") return evaluateExpression(testFormula.value === true ? node.consequent : node.alternate, state);
|
|
51355
|
+
const consequentFormula = getBooleanFormula(evaluateExpression(node.consequent, state));
|
|
51356
|
+
const alternateFormula = getBooleanFormula(evaluateExpression(node.alternate, state));
|
|
51357
|
+
return consequentFormula && alternateFormula ? booleanFormulaValue(conditionalBooleanFormula(testFormula, consequentFormula, alternateFormula)) : UNKNOWN_VALUE;
|
|
51358
|
+
}
|
|
51359
|
+
if (!isNodeOfType(node, "CallExpression")) return UNKNOWN_VALUE;
|
|
51360
|
+
const callee = stripParenExpression(node.callee);
|
|
51361
|
+
if (isNodeOfType(callee, "MemberExpression")) {
|
|
51362
|
+
const receiver = evaluateExpression(callee.object, state);
|
|
51363
|
+
const methodName = getStaticPropertyName(callee);
|
|
51364
|
+
const callback = node.arguments[0];
|
|
51365
|
+
if (receiver.kind === "empty-array" && (methodName === "every" || methodName === "some") && callback && !isNodeOfType(callback, "SpreadElement") && isProvablyCallable(callback, state)) return methodName === "every" ? TRUE_VALUE : FALSE_VALUE;
|
|
51366
|
+
if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Object" && state.scopes.isGlobalReference(callee.object) && (methodName === "keys" || methodName === "values") && node.arguments.length === 1 && !isNodeOfType(node.arguments[0], "SpreadElement")) {
|
|
51367
|
+
if (evaluateExpression(node.arguments[0], state).kind === "empty-object") return emptyReferenceValue("empty-array", node);
|
|
51368
|
+
}
|
|
51369
|
+
return UNKNOWN_VALUE;
|
|
51370
|
+
}
|
|
51371
|
+
if (!isNodeOfType(callee, "Identifier")) return UNKNOWN_VALUE;
|
|
51372
|
+
const localFunction = resolveExactLocalFunction(callee, state.scopes);
|
|
51373
|
+
if (!isFunctionLike$1(localFunction) || localFunction.async || localFunction.generator || state.activeFunctions.has(localFunction)) return UNKNOWN_VALUE;
|
|
51374
|
+
const returnedExpression = getReturnedExpression(localFunction);
|
|
51375
|
+
if (!returnedExpression) return UNKNOWN_VALUE;
|
|
51376
|
+
const parameters = localFunction.params ?? [];
|
|
51377
|
+
if (parameters.length !== node.arguments.length) return UNKNOWN_VALUE;
|
|
51378
|
+
const bindings = new Map(state.bindings);
|
|
51379
|
+
for (const [parameterIndex, parameter] of parameters.entries()) {
|
|
51380
|
+
const argument = node.arguments[parameterIndex];
|
|
51381
|
+
if (!isNodeOfType(parameter, "Identifier") || !argument || isNodeOfType(argument, "SpreadElement")) return UNKNOWN_VALUE;
|
|
51382
|
+
bindings.set(parameter.name, evaluateExpression(argument, state));
|
|
51383
|
+
}
|
|
51384
|
+
return evaluateExpression(returnedExpression, {
|
|
51385
|
+
...state,
|
|
51386
|
+
activeFunctions: new Set([...state.activeFunctions, localFunction]),
|
|
51387
|
+
bindings
|
|
51388
|
+
});
|
|
51389
|
+
};
|
|
51390
|
+
const comparatorProvesEmptyPropDoesNotBreakMemo = (comparatorExpression, propName, emptyLiteralKind, scopes) => {
|
|
51391
|
+
const comparator = resolveExactLocalFunction(comparatorExpression, scopes);
|
|
51392
|
+
if (!isFunctionLike$1(comparator) || comparator.async || comparator.generator) return false;
|
|
51393
|
+
const returnedExpression = getReturnedExpression(comparator);
|
|
51394
|
+
if (!returnedExpression) return false;
|
|
51395
|
+
const parameters = comparator.params ?? [];
|
|
51396
|
+
if (parameters.length !== 2 || !isNodeOfType(parameters[0], "Identifier") || !isNodeOfType(parameters[1], "Identifier")) return false;
|
|
51397
|
+
const bindings = new Map([[parameters[0].name, PREVIOUS_PROPS_VALUE], [parameters[1].name, NEXT_PROPS_VALUE]]);
|
|
51398
|
+
const evaluateComparator = (emptyReferencesAreEqual) => evaluateExpression(returnedExpression, {
|
|
51399
|
+
activeFunctions: new Set([comparator]),
|
|
51400
|
+
bindings,
|
|
51401
|
+
emptyLiteralKind,
|
|
51402
|
+
emptyReferencesAreEqual,
|
|
51403
|
+
propName,
|
|
51404
|
+
scopes
|
|
51405
|
+
});
|
|
51406
|
+
const distinctReferenceFormula = getBooleanFormula(evaluateComparator(false));
|
|
51407
|
+
const sharedReferenceFormula = getBooleanFormula(evaluateComparator(true));
|
|
51408
|
+
return Boolean(distinctReferenceFormula && sharedReferenceFormula && !couldStableTargetReferencePreventRender(distinctReferenceFormula, sharedReferenceFormula));
|
|
51409
|
+
};
|
|
51410
|
+
//#endregion
|
|
50186
51411
|
//#region src/plugin/rules/performance/rerender-memo-with-default-value.ts
|
|
50187
51412
|
const DEP_TAKING_HOOK_NAMES = new Set([
|
|
50188
51413
|
...HOOKS_WITH_DEPS,
|
|
@@ -50260,7 +51485,7 @@ const markCandidateIdentifier = (expression, candidateNames, shadowedNames, use,
|
|
|
50260
51485
|
if (!candidateNames.has(stripped.name)) return;
|
|
50261
51486
|
if (!into.has(stripped.name)) into.set(stripped.name, use);
|
|
50262
51487
|
};
|
|
50263
|
-
const collectIdentitySensitiveUses = (node, candidateNames, shadowedNames, memoRegistry, into) => {
|
|
51488
|
+
const collectIdentitySensitiveUses = (node, candidateNames, shadowedNames, memoRegistry, memoComparatorRegistry, defaultedBindings, context, into) => {
|
|
50264
51489
|
let innerShadowedNames = shadowedNames;
|
|
50265
51490
|
if (isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ArrowFunctionExpression")) {
|
|
50266
51491
|
const parameterNames = /* @__PURE__ */ new Set();
|
|
@@ -50275,12 +51500,21 @@ const collectIdentitySensitiveUses = (node, candidateNames, shadowedNames, memoR
|
|
|
50275
51500
|
if (isNodeOfType(node, "JSXOpeningElement")) {
|
|
50276
51501
|
const openingName = node.name;
|
|
50277
51502
|
const memoStatus = memoStatusForJsxOpeningName(memoRegistry, openingName);
|
|
50278
|
-
if (!isIntrinsicJsxElementName(openingName) && memoStatus !== "not-memoised")
|
|
50279
|
-
|
|
50280
|
-
|
|
50281
|
-
const
|
|
50282
|
-
|
|
50283
|
-
|
|
51503
|
+
if (!isIntrinsicJsxElementName(openingName) && memoStatus !== "not-memoised") {
|
|
51504
|
+
const comparatorDescriptor = isNodeOfType(openingName, "JSXIdentifier") ? memoComparatorRegistry.get(openingName.name) : void 0;
|
|
51505
|
+
const comparator = comparatorDescriptor && context.scopes.symbolFor(openingName)?.bindingIdentifier === comparatorDescriptor.bindingIdentifier ? comparatorDescriptor.comparator : void 0;
|
|
51506
|
+
for (const attribute of node.attributes ?? []) {
|
|
51507
|
+
if (!isNodeOfType(attribute, "JSXAttribute")) continue;
|
|
51508
|
+
if (!attribute.value || !isNodeOfType(attribute.value, "JSXExpressionContainer")) continue;
|
|
51509
|
+
const attributeExpression = attribute.value.expression;
|
|
51510
|
+
if (!attributeExpression || attributeExpression.type === "JSXEmptyExpression") continue;
|
|
51511
|
+
const strippedAttributeExpression = stripParenExpression(attributeExpression);
|
|
51512
|
+
if (comparator && isNodeOfType(attribute.name, "JSXIdentifier") && isNodeOfType(strippedAttributeExpression, "Identifier") && !innerShadowedNames.has(strippedAttributeExpression.name)) {
|
|
51513
|
+
const binding = defaultedBindings.get(strippedAttributeExpression.name);
|
|
51514
|
+
if (binding && comparatorProvesEmptyPropDoesNotBreakMemo(comparator, attribute.name.name, binding.literalKind, context.scopes)) continue;
|
|
51515
|
+
}
|
|
51516
|
+
markCandidateIdentifier(attributeExpression, candidateNames, innerShadowedNames, "memoized-prop", into);
|
|
51517
|
+
}
|
|
50284
51518
|
}
|
|
50285
51519
|
}
|
|
50286
51520
|
const nodeRecord = node;
|
|
@@ -50288,8 +51522,8 @@ const collectIdentitySensitiveUses = (node, candidateNames, shadowedNames, memoR
|
|
|
50288
51522
|
if (key === "parent") continue;
|
|
50289
51523
|
const child = nodeRecord[key];
|
|
50290
51524
|
if (Array.isArray(child)) {
|
|
50291
|
-
for (const item of child) if (isAstNode(item)) collectIdentitySensitiveUses(item, candidateNames, innerShadowedNames, memoRegistry, into);
|
|
50292
|
-
} else if (isAstNode(child)) collectIdentitySensitiveUses(child, candidateNames, innerShadowedNames, memoRegistry, into);
|
|
51525
|
+
for (const item of child) if (isAstNode(item)) collectIdentitySensitiveUses(item, candidateNames, innerShadowedNames, memoRegistry, memoComparatorRegistry, defaultedBindings, context, into);
|
|
51526
|
+
} else if (isAstNode(child)) collectIdentitySensitiveUses(child, candidateNames, innerShadowedNames, memoRegistry, memoComparatorRegistry, defaultedBindings, context, into);
|
|
50293
51527
|
}
|
|
50294
51528
|
};
|
|
50295
51529
|
const buildMessage$2 = (literalKind, use) => {
|
|
@@ -50306,13 +51540,14 @@ const rerenderMemoWithDefaultValue = defineRule({
|
|
|
50306
51540
|
recommendation: "Move it to the top of the file: `const EMPTY_ITEMS: Item[] = []`, then use that as the default value",
|
|
50307
51541
|
create: (context) => {
|
|
50308
51542
|
let memoRegistry = /* @__PURE__ */ new Map();
|
|
51543
|
+
let memoComparatorRegistry = /* @__PURE__ */ new Map();
|
|
50309
51544
|
const checkComponentFunction = (functionNode) => {
|
|
50310
51545
|
if (!isNodeOfType(functionNode, "FunctionDeclaration") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "ArrowFunctionExpression")) return;
|
|
50311
51546
|
const defaultedBindings = collectDefaultedEmptyBindings(functionNode);
|
|
50312
51547
|
if (defaultedBindings.size === 0) return;
|
|
50313
51548
|
if (!functionNode.body) return;
|
|
50314
51549
|
const identitySensitiveUses = /* @__PURE__ */ new Map();
|
|
50315
|
-
collectIdentitySensitiveUses(functionNode.body, new Set(defaultedBindings.keys()), /* @__PURE__ */ new Set(), memoRegistry, identitySensitiveUses);
|
|
51550
|
+
collectIdentitySensitiveUses(functionNode.body, new Set(defaultedBindings.keys()), /* @__PURE__ */ new Set(), memoRegistry, memoComparatorRegistry, defaultedBindings, context, identitySensitiveUses);
|
|
50316
51551
|
for (const [bindingName, binding] of defaultedBindings) {
|
|
50317
51552
|
const use = identitySensitiveUses.get(bindingName);
|
|
50318
51553
|
if (!use) continue;
|
|
@@ -50325,6 +51560,17 @@ const rerenderMemoWithDefaultValue = defineRule({
|
|
|
50325
51560
|
return {
|
|
50326
51561
|
Program(node) {
|
|
50327
51562
|
memoRegistry = buildSameFileMemoRegistry(node);
|
|
51563
|
+
memoComparatorRegistry = buildSameFileMemoComparatorRegistry(node);
|
|
51564
|
+
},
|
|
51565
|
+
CallExpression(node) {
|
|
51566
|
+
for (const finding of findForwardedFreshHookDependencies(node, context)) {
|
|
51567
|
+
if (finding.origin !== "default") continue;
|
|
51568
|
+
if (finding.kind !== "array" && finding.kind !== "object") continue;
|
|
51569
|
+
context.report({
|
|
51570
|
+
node: finding.reportNode,
|
|
51571
|
+
message: `This custom Hook default creates a new ${finding.kind} every render and forwards it to a Hook dependency.`
|
|
51572
|
+
});
|
|
51573
|
+
}
|
|
50328
51574
|
},
|
|
50329
51575
|
FunctionDeclaration(node) {
|
|
50330
51576
|
if (!node.id?.name || !isUppercaseName(node.id.name)) return;
|
|
@@ -50414,7 +51660,7 @@ const isSetterCalledDuringRender = (root, setterName) => {
|
|
|
50414
51660
|
let found = false;
|
|
50415
51661
|
walkAst(root, (child) => {
|
|
50416
51662
|
if (found) return false;
|
|
50417
|
-
if (child !== root && isFunctionLike$
|
|
51663
|
+
if (child !== root && isFunctionLike$1(child)) return false;
|
|
50418
51664
|
if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && child.callee.name === setterName) {
|
|
50419
51665
|
found = true;
|
|
50420
51666
|
return false;
|
|
@@ -50454,7 +51700,7 @@ const collectEffectDependencyInfos = (componentBody, setterNames) => {
|
|
|
50454
51700
|
const effectCallback = child.arguments?.[0];
|
|
50455
51701
|
if (isNodeOfType(effectCallback, "ArrowFunctionExpression") || isNodeOfType(effectCallback, "FunctionExpression")) {
|
|
50456
51702
|
walkAst(effectCallback.body, (bodyNode) => {
|
|
50457
|
-
if (bodyNode !== effectCallback.body && isFunctionLike$
|
|
51703
|
+
if (bodyNode !== effectCallback.body && isFunctionLike$1(bodyNode)) return false;
|
|
50458
51704
|
if (isNodeOfType(bodyNode, "CallExpression") && isNodeOfType(bodyNode.callee, "Identifier")) synchronouslyCalledFunctionNames.add(bodyNode.callee.name);
|
|
50459
51705
|
});
|
|
50460
51706
|
walkAst(effectCallback.body, (bodyNode) => {
|
|
@@ -50462,7 +51708,7 @@ const collectEffectDependencyInfos = (componentBody, setterNames) => {
|
|
|
50462
51708
|
if (!isNodeOfType(bodyNode.callee, "Identifier")) return;
|
|
50463
51709
|
let ancestor = bodyNode.parent;
|
|
50464
51710
|
while (ancestor && ancestor !== effectCallback.body) {
|
|
50465
|
-
if (isFunctionLike$
|
|
51711
|
+
if (isFunctionLike$1(ancestor)) {
|
|
50466
51712
|
nestedCallbackCalledFunctionNames.add(bodyNode.callee.name);
|
|
50467
51713
|
return;
|
|
50468
51714
|
}
|
|
@@ -50523,7 +51769,7 @@ const hasRenderPhaseNonHookCall = (componentBody) => {
|
|
|
50523
51769
|
let didFindRenderPhaseCall = false;
|
|
50524
51770
|
walkAst(componentBody, (child) => {
|
|
50525
51771
|
if (didFindRenderPhaseCall) return false;
|
|
50526
|
-
if (child !== componentBody && isFunctionLike$
|
|
51772
|
+
if (child !== componentBody && isFunctionLike$1(child)) return false;
|
|
50527
51773
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
50528
51774
|
const calleeName = isNodeOfType(child.callee, "Identifier") ? child.callee.name : isNodeOfType(child.callee, "MemberExpression") && isNodeOfType(child.callee.property, "Identifier") ? child.callee.property.name : null;
|
|
50529
51775
|
if (calleeName !== null && isReactHookName(calleeName)) return;
|
|
@@ -52627,7 +53873,7 @@ const isReduceBuildingJsxRows = (node) => {
|
|
|
52627
53873
|
if (!isNodeOfType(node.callee, "MemberExpression")) return false;
|
|
52628
53874
|
if (!isNodeOfType(node.callee.property, "Identifier") || node.callee.property.name !== "reduce") return false;
|
|
52629
53875
|
const rowBuilder = node.arguments?.[0];
|
|
52630
|
-
if (!rowBuilder || !isFunctionLike$
|
|
53876
|
+
if (!rowBuilder || !isFunctionLike$1(rowBuilder)) return false;
|
|
52631
53877
|
let buildsJsx = false;
|
|
52632
53878
|
walkAst(rowBuilder, (child) => {
|
|
52633
53879
|
if (isNodeOfType(child, "JSXElement")) buildsJsx = true;
|
|
@@ -56852,7 +58098,7 @@ const countOwnScopeHookCalls = (functionNode, scopes, settings, countedFunctionN
|
|
|
56852
58098
|
let count = 0;
|
|
56853
58099
|
walkAst(functionNode, (child) => {
|
|
56854
58100
|
if (child === functionNode) return;
|
|
56855
|
-
if (isFunctionLike$
|
|
58101
|
+
if (isFunctionLike$1(child)) return false;
|
|
56856
58102
|
if (!isHookCall$1(child, scopes, settings)) return;
|
|
56857
58103
|
if (isNodeOfType(child, "CallExpression") && isLocalNonHookFunctionCallee(child, scopes, settings, countedFunctionNodes)) return;
|
|
56858
58104
|
count += 1;
|
|
@@ -56866,7 +58112,7 @@ const isLocalNonHookFunctionCallee = (call, scopes, settings, countedFunctionNod
|
|
|
56866
58112
|
if (!isNodeOfType(callee, "Identifier")) return false;
|
|
56867
58113
|
const symbol = scopes.symbolFor(callee);
|
|
56868
58114
|
if (!symbol) return false;
|
|
56869
|
-
const localFunction = symbol.initializer && isFunctionLike$
|
|
58115
|
+
const localFunction = symbol.initializer && isFunctionLike$1(symbol.initializer) ? symbol.initializer : null;
|
|
56870
58116
|
if (!localFunction) return false;
|
|
56871
58117
|
return countOwnScopeHookCalls(localFunction, scopes, settings, countedFunctionNodes) === 0;
|
|
56872
58118
|
};
|
|
@@ -56941,7 +58187,7 @@ const isInsideLoop = (descendant, ancestor) => {
|
|
|
56941
58187
|
const findEnclosingComponentOrHookFunction = (node) => {
|
|
56942
58188
|
let current = node.parent;
|
|
56943
58189
|
while (current) {
|
|
56944
|
-
if (isFunctionLike$
|
|
58190
|
+
if (isFunctionLike$1(current)) {
|
|
56945
58191
|
if (isReactHocCallbackArgument(current)) return current;
|
|
56946
58192
|
const resolvedName = inferFunctionName(current);
|
|
56947
58193
|
if (resolvedName !== null && isComponentOrHookDisplayName(resolvedName, current)) return current;
|
|
@@ -56963,7 +58209,7 @@ const isCallbackArgumentForAllowedEffectEventHook = (functionNode, additionalEff
|
|
|
56963
58209
|
const isInsideAllowedEffectEventCallback = (node, additionalEffectHooksRegex) => {
|
|
56964
58210
|
let current = node.parent;
|
|
56965
58211
|
while (current) {
|
|
56966
|
-
if (isFunctionLike$
|
|
58212
|
+
if (isFunctionLike$1(current) && isCallbackArgumentForAllowedEffectEventHook(current, additionalEffectHooksRegex)) return true;
|
|
56967
58213
|
current = current.parent ?? null;
|
|
56968
58214
|
}
|
|
56969
58215
|
return false;
|
|
@@ -57379,7 +58625,7 @@ const isNonPrivilegedServerAction = (functionNode) => {
|
|
|
57379
58625
|
let hasPrivilegedEffect = false;
|
|
57380
58626
|
walkAst(functionBody, (child) => {
|
|
57381
58627
|
if (hasPrivilegedEffect) return false;
|
|
57382
|
-
if (child !== functionBody && isFunctionLike$
|
|
58628
|
+
if (child !== functionBody && isFunctionLike$1(child)) return false;
|
|
57383
58629
|
if (isCacheOrNavigationCall(child)) {
|
|
57384
58630
|
hasNonDataEffectCall = true;
|
|
57385
58631
|
return;
|
|
@@ -57451,7 +58697,7 @@ const containsAuthCheck = (rootNodes, allowedFunctionNames, genericMethodNames)
|
|
|
57451
58697
|
let foundAuthCall = false;
|
|
57452
58698
|
for (const rootNode of rootNodes) walkAst(rootNode, (child) => {
|
|
57453
58699
|
if (foundAuthCall) return;
|
|
57454
|
-
if (isFunctionLike$
|
|
58700
|
+
if (isFunctionLike$1(child)) return false;
|
|
57455
58701
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
57456
58702
|
if (getAuthCallName(child, allowedFunctionNames, genericMethodNames)) foundAuthCall = true;
|
|
57457
58703
|
});
|
|
@@ -57902,7 +59148,7 @@ const serverHoistStaticIo = defineRule({
|
|
|
57902
59148
|
ExportDefaultDeclaration(node) {
|
|
57903
59149
|
if (!isInProjectDirectory(context, "pages/api")) return;
|
|
57904
59150
|
const declaration = node.declaration;
|
|
57905
|
-
if (!isFunctionLike$
|
|
59151
|
+
if (!isFunctionLike$1(declaration)) return;
|
|
57906
59152
|
if (!declaration.async) return;
|
|
57907
59153
|
const body = declaration.body;
|
|
57908
59154
|
if (!isNodeOfType(body, "BlockStatement")) return;
|
|
@@ -58085,7 +59331,7 @@ const isKnownPropertyObjectMutation = (callExpression, methodName, writablePrope
|
|
|
58085
59331
|
const runsAfterModuleInitialization = (node, boundaryNode = null) => {
|
|
58086
59332
|
let ancestor = node.parent;
|
|
58087
59333
|
while (ancestor && ancestor !== boundaryNode) {
|
|
58088
|
-
if (isFunctionLike$
|
|
59334
|
+
if (isFunctionLike$1(ancestor) && !isImmediatelyInvokedFunction(ancestor)) return true;
|
|
58089
59335
|
ancestor = ancestor.parent ?? null;
|
|
58090
59336
|
}
|
|
58091
59337
|
return false;
|
|
@@ -58105,7 +59351,7 @@ const isMutatedThroughCallArgument = (referenceIdentifier, scopes, mayFollowCall
|
|
|
58105
59351
|
const calleeSymbol = scopes.symbolFor(callee);
|
|
58106
59352
|
if (!calleeSymbol) return false;
|
|
58107
59353
|
const calleeFunction = calleeSymbol.initializer;
|
|
58108
|
-
if (!isFunctionLike$
|
|
59354
|
+
if (!isFunctionLike$1(calleeFunction)) return false;
|
|
58109
59355
|
const parameter = calleeFunction.params?.[referenceArgumentIndex];
|
|
58110
59356
|
if (!parameter || !isNodeOfType(parameter, "Identifier")) return false;
|
|
58111
59357
|
const parameterSymbol = scopes.symbolFor(parameter);
|
|
@@ -59287,8 +60533,8 @@ const tanstackStartNoNavigateInRender = defineRule({
|
|
|
59287
60533
|
};
|
|
59288
60534
|
const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && REACT_HANDLER_PROP_PATTERN.test(node.name.name);
|
|
59289
60535
|
const isEventHandlerNamedProperty = (node) => isNodeOfType(node, "Property") && (isNodeOfType(node.key, "Identifier") && typeof node.key.name === "string" && REACT_HANDLER_PROP_PATTERN.test(node.key.name) || isNodeOfType(node.key, "Literal") && typeof node.key.value === "string" && REACT_HANDLER_PROP_PATTERN.test(node.key.value));
|
|
59290
|
-
const isEventHandlerProperty = (node) => isNodeOfType(node, "Property") && isFunctionLike$
|
|
59291
|
-
const isHandlerNamedVariableDeclarator = (node) => isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && typeof node.id.name === "string" && HANDLER_FUNCTION_NAME_PATTERN.test(node.id.name) && isFunctionLike$
|
|
60536
|
+
const isEventHandlerProperty = (node) => isNodeOfType(node, "Property") && isFunctionLike$1(node.value) && isEventHandlerNamedProperty(node);
|
|
60537
|
+
const isHandlerNamedVariableDeclarator = (node) => isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && typeof node.id.name === "string" && HANDLER_FUNCTION_NAME_PATTERN.test(node.id.name) && isFunctionLike$1(node.init);
|
|
59292
60538
|
const isHandlerNamedFunctionDeclaration = (node) => isNodeOfType(node, "FunctionDeclaration") && isNodeOfType(node.id, "Identifier") && typeof node.id.name === "string" && HANDLER_FUNCTION_NAME_PATTERN.test(node.id.name);
|
|
59293
60539
|
const isInsideEventHandlerValue = (identifier) => {
|
|
59294
60540
|
let cursor = identifier.parent;
|
|
@@ -59466,7 +60712,7 @@ const tanstackStartNoUseEffectFetch = defineRule({
|
|
|
59466
60712
|
const effectInvokedFunctions = collectEffectInvokedFunctions(callback);
|
|
59467
60713
|
walkAst(callback, (child) => {
|
|
59468
60714
|
if (hasFetchCall) return false;
|
|
59469
|
-
if (child !== callback && isFunctionLike$
|
|
60715
|
+
if (child !== callback && isFunctionLike$1(child) && !effectInvokedFunctions.has(child)) return false;
|
|
59470
60716
|
if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && child.callee.name === "fetch") hasFetchCall = true;
|
|
59471
60717
|
});
|
|
59472
60718
|
if (hasFetchCall) context.report({
|
|
@@ -65041,387 +66287,6 @@ const wrapReactNativeRule = (rule) => {
|
|
|
65041
66287
|
};
|
|
65042
66288
|
};
|
|
65043
66289
|
//#endregion
|
|
65044
|
-
//#region src/plugin/semantic/control-flow-graph.ts
|
|
65045
|
-
let nextBlockId = 0;
|
|
65046
|
-
const createBlock = (builder) => {
|
|
65047
|
-
const block = {
|
|
65048
|
-
id: nextBlockId++,
|
|
65049
|
-
nodes: [],
|
|
65050
|
-
successors: [],
|
|
65051
|
-
predecessors: []
|
|
65052
|
-
};
|
|
65053
|
-
builder.blocks.push(block);
|
|
65054
|
-
return block;
|
|
65055
|
-
};
|
|
65056
|
-
const addEdge = (from, to, kind) => {
|
|
65057
|
-
const edge = {
|
|
65058
|
-
from,
|
|
65059
|
-
to,
|
|
65060
|
-
kind
|
|
65061
|
-
};
|
|
65062
|
-
from.successors.push(edge);
|
|
65063
|
-
to.predecessors.push(edge);
|
|
65064
|
-
};
|
|
65065
|
-
const appendNode = (builder, block, node) => {
|
|
65066
|
-
block.nodes.push(node);
|
|
65067
|
-
if (!builder.nodeBlock.has(node)) builder.nodeBlock.set(node, block);
|
|
65068
|
-
};
|
|
65069
|
-
const mapDescendantsToBlock = (builder, node, block) => {
|
|
65070
|
-
builder.nodeBlock.set(node, block);
|
|
65071
|
-
if (isFunctionLike$2(node)) {
|
|
65072
|
-
builder.nestedFunctions.push(node);
|
|
65073
|
-
return;
|
|
65074
|
-
}
|
|
65075
|
-
const record = node;
|
|
65076
|
-
for (const key of Object.keys(record)) {
|
|
65077
|
-
if (key === "parent") continue;
|
|
65078
|
-
const child = record[key];
|
|
65079
|
-
if (Array.isArray(child)) {
|
|
65080
|
-
for (const item of child) if (isAstNode(item)) mapDescendantsToBlock(builder, item, block);
|
|
65081
|
-
} else if (isAstNode(child)) mapDescendantsToBlock(builder, child, block);
|
|
65082
|
-
}
|
|
65083
|
-
};
|
|
65084
|
-
const hasInternalControlFlow = (node) => {
|
|
65085
|
-
switch (node.type) {
|
|
65086
|
-
case "IfStatement":
|
|
65087
|
-
case "WhileStatement":
|
|
65088
|
-
case "DoWhileStatement":
|
|
65089
|
-
case "ForStatement":
|
|
65090
|
-
case "ForInStatement":
|
|
65091
|
-
case "ForOfStatement":
|
|
65092
|
-
case "SwitchStatement":
|
|
65093
|
-
case "TryStatement":
|
|
65094
|
-
case "ReturnStatement":
|
|
65095
|
-
case "ThrowStatement":
|
|
65096
|
-
case "BreakStatement":
|
|
65097
|
-
case "ContinueStatement":
|
|
65098
|
-
case "BlockStatement":
|
|
65099
|
-
case "LabeledStatement": return true;
|
|
65100
|
-
default: return false;
|
|
65101
|
-
}
|
|
65102
|
-
};
|
|
65103
|
-
const findLabel = (builder, name) => {
|
|
65104
|
-
if (name === null) {
|
|
65105
|
-
if (builder.loopStack.length > 0) {
|
|
65106
|
-
const top = builder.loopStack[builder.loopStack.length - 1];
|
|
65107
|
-
return {
|
|
65108
|
-
merge: top.merge,
|
|
65109
|
-
header: top.header
|
|
65110
|
-
};
|
|
65111
|
-
}
|
|
65112
|
-
if (builder.switchStack.length > 0) return {
|
|
65113
|
-
merge: builder.switchStack[builder.switchStack.length - 1].merge,
|
|
65114
|
-
header: null
|
|
65115
|
-
};
|
|
65116
|
-
return null;
|
|
65117
|
-
}
|
|
65118
|
-
for (let i = builder.labelStack.length - 1; i >= 0; i--) {
|
|
65119
|
-
const entry = builder.labelStack[i];
|
|
65120
|
-
if (entry.label === name) return {
|
|
65121
|
-
merge: entry.merge,
|
|
65122
|
-
header: entry.header
|
|
65123
|
-
};
|
|
65124
|
-
}
|
|
65125
|
-
return null;
|
|
65126
|
-
};
|
|
65127
|
-
const buildStatements = (builder, statements, current) => {
|
|
65128
|
-
let cursor = current;
|
|
65129
|
-
for (const statement of statements) cursor = buildStatement(builder, statement, cursor);
|
|
65130
|
-
return cursor;
|
|
65131
|
-
};
|
|
65132
|
-
const buildStatement = (builder, statement, current) => {
|
|
65133
|
-
builder.nodeBlock.set(statement, current);
|
|
65134
|
-
if (!hasInternalControlFlow(statement)) {
|
|
65135
|
-
appendNode(builder, current, statement);
|
|
65136
|
-
mapDescendantsToBlock(builder, statement, current);
|
|
65137
|
-
return current;
|
|
65138
|
-
}
|
|
65139
|
-
if (isNodeOfType(statement, "BlockStatement")) return buildStatements(builder, statement.body, current);
|
|
65140
|
-
if (isNodeOfType(statement, "LabeledStatement")) {
|
|
65141
|
-
const merge = createBlock(builder);
|
|
65142
|
-
const labelEntry = {
|
|
65143
|
-
label: statement.label.name,
|
|
65144
|
-
merge,
|
|
65145
|
-
header: null
|
|
65146
|
-
};
|
|
65147
|
-
builder.labelStack.push(labelEntry);
|
|
65148
|
-
const body = statement.body;
|
|
65149
|
-
const end = buildStatement(builder, body, current);
|
|
65150
|
-
builder.labelStack.pop();
|
|
65151
|
-
addEdge(end, merge, "uncond");
|
|
65152
|
-
return merge;
|
|
65153
|
-
}
|
|
65154
|
-
if (isNodeOfType(statement, "ReturnStatement")) {
|
|
65155
|
-
if (statement.argument) mapDescendantsToBlock(builder, statement.argument, current);
|
|
65156
|
-
addEdge(current, builder.exit, "uncond");
|
|
65157
|
-
return createBlock(builder);
|
|
65158
|
-
}
|
|
65159
|
-
if (isNodeOfType(statement, "ThrowStatement")) {
|
|
65160
|
-
if (statement.argument) mapDescendantsToBlock(builder, statement.argument, current);
|
|
65161
|
-
const top = builder.tryStack[builder.tryStack.length - 1];
|
|
65162
|
-
if (top?.catch) addEdge(current, top.catch, "uncond");
|
|
65163
|
-
else if (top?.finally) addEdge(current, top.finally, "uncond");
|
|
65164
|
-
else addEdge(current, builder.exit, "throw");
|
|
65165
|
-
return createBlock(builder);
|
|
65166
|
-
}
|
|
65167
|
-
if (isNodeOfType(statement, "BreakStatement")) {
|
|
65168
|
-
const target = findLabel(builder, statement.label ? statement.label.name : null);
|
|
65169
|
-
if (target) addEdge(current, target.merge, "uncond");
|
|
65170
|
-
else addEdge(current, builder.exit, "uncond");
|
|
65171
|
-
return createBlock(builder);
|
|
65172
|
-
}
|
|
65173
|
-
if (isNodeOfType(statement, "ContinueStatement")) {
|
|
65174
|
-
const target = findLabel(builder, statement.label ? statement.label.name : null);
|
|
65175
|
-
if (target?.header) addEdge(current, target.header, "uncond");
|
|
65176
|
-
return createBlock(builder);
|
|
65177
|
-
}
|
|
65178
|
-
if (isNodeOfType(statement, "IfStatement")) {
|
|
65179
|
-
mapDescendantsToBlock(builder, statement.test, current);
|
|
65180
|
-
const thenBlock = createBlock(builder);
|
|
65181
|
-
const merge = createBlock(builder);
|
|
65182
|
-
addEdge(current, thenBlock, "cond");
|
|
65183
|
-
addEdge(buildStatement(builder, statement.consequent, thenBlock), merge, "uncond");
|
|
65184
|
-
if (statement.alternate) {
|
|
65185
|
-
const elseBlock = createBlock(builder);
|
|
65186
|
-
addEdge(current, elseBlock, "cond");
|
|
65187
|
-
addEdge(buildStatement(builder, statement.alternate, elseBlock), merge, "uncond");
|
|
65188
|
-
} else addEdge(current, merge, "cond");
|
|
65189
|
-
return merge;
|
|
65190
|
-
}
|
|
65191
|
-
if (isNodeOfType(statement, "WhileStatement") || isNodeOfType(statement, "DoWhileStatement")) {
|
|
65192
|
-
const isDoWhile = isNodeOfType(statement, "DoWhileStatement");
|
|
65193
|
-
mapDescendantsToBlock(builder, statement.test, current);
|
|
65194
|
-
const header = createBlock(builder);
|
|
65195
|
-
const body = createBlock(builder);
|
|
65196
|
-
const merge = createBlock(builder);
|
|
65197
|
-
if (isDoWhile) addEdge(current, body, "uncond");
|
|
65198
|
-
else {
|
|
65199
|
-
addEdge(current, header, "uncond");
|
|
65200
|
-
addEdge(header, body, "cond");
|
|
65201
|
-
addEdge(header, merge, "cond");
|
|
65202
|
-
}
|
|
65203
|
-
builder.loopStack.push({
|
|
65204
|
-
header,
|
|
65205
|
-
merge,
|
|
65206
|
-
label: null
|
|
65207
|
-
});
|
|
65208
|
-
const bodyEnd = buildStatement(builder, statement.body, body);
|
|
65209
|
-
builder.loopStack.pop();
|
|
65210
|
-
if (isDoWhile) {
|
|
65211
|
-
addEdge(bodyEnd, header, "uncond");
|
|
65212
|
-
addEdge(header, body, "cond");
|
|
65213
|
-
addEdge(header, merge, "cond");
|
|
65214
|
-
} else addEdge(bodyEnd, header, "uncond");
|
|
65215
|
-
return merge;
|
|
65216
|
-
}
|
|
65217
|
-
if (isNodeOfType(statement, "ForStatement")) {
|
|
65218
|
-
if (statement.init) mapDescendantsToBlock(builder, statement.init, current);
|
|
65219
|
-
if (statement.test) mapDescendantsToBlock(builder, statement.test, current);
|
|
65220
|
-
const header = createBlock(builder);
|
|
65221
|
-
const body = createBlock(builder);
|
|
65222
|
-
const merge = createBlock(builder);
|
|
65223
|
-
addEdge(current, header, "uncond");
|
|
65224
|
-
addEdge(header, body, "cond");
|
|
65225
|
-
addEdge(header, merge, "cond");
|
|
65226
|
-
builder.loopStack.push({
|
|
65227
|
-
header,
|
|
65228
|
-
merge,
|
|
65229
|
-
label: null
|
|
65230
|
-
});
|
|
65231
|
-
const bodyEnd = buildStatement(builder, statement.body, body);
|
|
65232
|
-
builder.loopStack.pop();
|
|
65233
|
-
if (statement.update) mapDescendantsToBlock(builder, statement.update, header);
|
|
65234
|
-
addEdge(bodyEnd, header, "uncond");
|
|
65235
|
-
return merge;
|
|
65236
|
-
}
|
|
65237
|
-
if (isNodeOfType(statement, "ForInStatement") || isNodeOfType(statement, "ForOfStatement")) {
|
|
65238
|
-
mapDescendantsToBlock(builder, statement.right, current);
|
|
65239
|
-
mapDescendantsToBlock(builder, statement.left, current);
|
|
65240
|
-
const header = createBlock(builder);
|
|
65241
|
-
const body = createBlock(builder);
|
|
65242
|
-
const merge = createBlock(builder);
|
|
65243
|
-
addEdge(current, header, "uncond");
|
|
65244
|
-
addEdge(header, body, "cond");
|
|
65245
|
-
addEdge(header, merge, "cond");
|
|
65246
|
-
builder.loopStack.push({
|
|
65247
|
-
header,
|
|
65248
|
-
merge,
|
|
65249
|
-
label: null
|
|
65250
|
-
});
|
|
65251
|
-
const bodyEnd = buildStatement(builder, statement.body, body);
|
|
65252
|
-
builder.loopStack.pop();
|
|
65253
|
-
addEdge(bodyEnd, header, "uncond");
|
|
65254
|
-
return merge;
|
|
65255
|
-
}
|
|
65256
|
-
if (isNodeOfType(statement, "SwitchStatement")) {
|
|
65257
|
-
mapDescendantsToBlock(builder, statement.discriminant, current);
|
|
65258
|
-
const merge = createBlock(builder);
|
|
65259
|
-
builder.switchStack.push({
|
|
65260
|
-
merge,
|
|
65261
|
-
label: null
|
|
65262
|
-
});
|
|
65263
|
-
let previousCaseEnd = null;
|
|
65264
|
-
let hasDefault = false;
|
|
65265
|
-
for (const switchCase of statement.cases) {
|
|
65266
|
-
const caseBlock = createBlock(builder);
|
|
65267
|
-
addEdge(current, caseBlock, "cond");
|
|
65268
|
-
if (previousCaseEnd) addEdge(previousCaseEnd, caseBlock, "uncond");
|
|
65269
|
-
previousCaseEnd = buildStatements(builder, switchCase.consequent, caseBlock);
|
|
65270
|
-
if (switchCase.test === null) hasDefault = true;
|
|
65271
|
-
}
|
|
65272
|
-
builder.switchStack.pop();
|
|
65273
|
-
if (previousCaseEnd) addEdge(previousCaseEnd, merge, "uncond");
|
|
65274
|
-
if (!hasDefault) addEdge(current, merge, "cond");
|
|
65275
|
-
return merge;
|
|
65276
|
-
}
|
|
65277
|
-
if (isNodeOfType(statement, "TryStatement")) {
|
|
65278
|
-
const tryBlock = createBlock(builder);
|
|
65279
|
-
const merge = createBlock(builder);
|
|
65280
|
-
let catchBlock = null;
|
|
65281
|
-
let finallyBlock = null;
|
|
65282
|
-
if (statement.handler) catchBlock = createBlock(builder);
|
|
65283
|
-
if (statement.finalizer) finallyBlock = createBlock(builder);
|
|
65284
|
-
addEdge(current, tryBlock, "uncond");
|
|
65285
|
-
builder.tryStack.push({
|
|
65286
|
-
catch: catchBlock,
|
|
65287
|
-
finally: finallyBlock
|
|
65288
|
-
});
|
|
65289
|
-
const tryEnd = buildStatements(builder, statement.block.body, tryBlock);
|
|
65290
|
-
builder.tryStack.pop();
|
|
65291
|
-
if (catchBlock) addEdge(tryBlock, catchBlock, "cond");
|
|
65292
|
-
if (statement.handler && catchBlock) {
|
|
65293
|
-
const handlerBody = statement.handler.body;
|
|
65294
|
-
const catchEnd = buildStatement(builder, handlerBody, catchBlock);
|
|
65295
|
-
if (finallyBlock) addEdge(catchEnd, finallyBlock, "uncond");
|
|
65296
|
-
else addEdge(catchEnd, merge, "uncond");
|
|
65297
|
-
}
|
|
65298
|
-
if (finallyBlock && statement.finalizer) {
|
|
65299
|
-
addEdge(tryEnd, finallyBlock, "uncond");
|
|
65300
|
-
addEdge(buildStatements(builder, statement.finalizer.body, finallyBlock), merge, "uncond");
|
|
65301
|
-
} else addEdge(tryEnd, merge, "uncond");
|
|
65302
|
-
return merge;
|
|
65303
|
-
}
|
|
65304
|
-
appendNode(builder, current, statement);
|
|
65305
|
-
mapDescendantsToBlock(builder, statement, current);
|
|
65306
|
-
return current;
|
|
65307
|
-
};
|
|
65308
|
-
const buildFunctionCfg = (functionNode, body, nestedFunctionSink) => {
|
|
65309
|
-
const builder = {
|
|
65310
|
-
blocks: [],
|
|
65311
|
-
entry: null,
|
|
65312
|
-
exit: null,
|
|
65313
|
-
nestedFunctions: nestedFunctionSink,
|
|
65314
|
-
nodeBlock: /* @__PURE__ */ new Map(),
|
|
65315
|
-
loopStack: [],
|
|
65316
|
-
switchStack: [],
|
|
65317
|
-
tryStack: [],
|
|
65318
|
-
labelStack: []
|
|
65319
|
-
};
|
|
65320
|
-
const entry = createBlock(builder);
|
|
65321
|
-
const exit = createBlock(builder);
|
|
65322
|
-
builder.entry = entry;
|
|
65323
|
-
builder.exit = exit;
|
|
65324
|
-
let bodyEnd;
|
|
65325
|
-
if (isNodeOfType(body, "BlockStatement")) bodyEnd = buildStatements(builder, body.body, entry);
|
|
65326
|
-
else {
|
|
65327
|
-
mapDescendantsToBlock(builder, body, entry);
|
|
65328
|
-
bodyEnd = entry;
|
|
65329
|
-
}
|
|
65330
|
-
addEdge(bodyEnd, exit, "uncond");
|
|
65331
|
-
return {
|
|
65332
|
-
owner: functionNode,
|
|
65333
|
-
entry,
|
|
65334
|
-
exit,
|
|
65335
|
-
blocks: builder.blocks,
|
|
65336
|
-
blockOf: (node) => builder.nodeBlock.get(node) ?? null
|
|
65337
|
-
};
|
|
65338
|
-
};
|
|
65339
|
-
const computeUnconditionalSet = (cfg) => {
|
|
65340
|
-
const reachableFromEntry = (excluded) => {
|
|
65341
|
-
const visited = /* @__PURE__ */ new Set();
|
|
65342
|
-
const queue = [];
|
|
65343
|
-
if (cfg.entry !== excluded) queue.push(cfg.entry);
|
|
65344
|
-
while (queue.length > 0) {
|
|
65345
|
-
const block = queue.shift();
|
|
65346
|
-
if (visited.has(block)) continue;
|
|
65347
|
-
visited.add(block);
|
|
65348
|
-
for (const edge of block.successors) {
|
|
65349
|
-
if (edge.kind === "throw") continue;
|
|
65350
|
-
if (edge.to === excluded) continue;
|
|
65351
|
-
queue.push(edge.to);
|
|
65352
|
-
}
|
|
65353
|
-
}
|
|
65354
|
-
return visited;
|
|
65355
|
-
};
|
|
65356
|
-
const reachableFromEntryFull = reachableFromEntry(null);
|
|
65357
|
-
const unconditional = /* @__PURE__ */ new Set();
|
|
65358
|
-
unconditional.add(cfg.entry);
|
|
65359
|
-
unconditional.add(cfg.exit);
|
|
65360
|
-
for (const block of cfg.blocks) {
|
|
65361
|
-
if (unconditional.has(block)) continue;
|
|
65362
|
-
if (!reachableFromEntryFull.has(block)) {
|
|
65363
|
-
unconditional.add(block);
|
|
65364
|
-
continue;
|
|
65365
|
-
}
|
|
65366
|
-
if (!reachableFromEntry(block).has(cfg.exit)) unconditional.add(block);
|
|
65367
|
-
}
|
|
65368
|
-
return unconditional;
|
|
65369
|
-
};
|
|
65370
|
-
const analyzeControlFlow = (program) => {
|
|
65371
|
-
nextBlockId = 0;
|
|
65372
|
-
const functionCfgs = /* @__PURE__ */ new Map();
|
|
65373
|
-
const pendingFunctions = [];
|
|
65374
|
-
const buildFor = (functionNode, body) => {
|
|
65375
|
-
const cfg = buildFunctionCfg(functionNode, body, pendingFunctions);
|
|
65376
|
-
functionCfgs.set(functionNode, {
|
|
65377
|
-
cfg,
|
|
65378
|
-
unconditionalSet: computeUnconditionalSet(cfg)
|
|
65379
|
-
});
|
|
65380
|
-
};
|
|
65381
|
-
if (isNodeOfType(program, "Program")) buildFor(program, {
|
|
65382
|
-
type: "BlockStatement",
|
|
65383
|
-
body: program.body
|
|
65384
|
-
});
|
|
65385
|
-
for (let functionIndex = 0; functionIndex < pendingFunctions.length; functionIndex += 1) {
|
|
65386
|
-
const functionNode = pendingFunctions[functionIndex];
|
|
65387
|
-
if (!isFunctionLike$2(functionNode) || !functionNode.body || functionCfgs.has(functionNode)) continue;
|
|
65388
|
-
buildFor(functionNode, functionNode.body);
|
|
65389
|
-
}
|
|
65390
|
-
const getFunctionEntry = (functionNode) => {
|
|
65391
|
-
const existingEntry = functionCfgs.get(functionNode);
|
|
65392
|
-
if (existingEntry) return existingEntry;
|
|
65393
|
-
if (!isFunctionLike$2(functionNode) || !functionNode.body) return null;
|
|
65394
|
-
buildFor(functionNode, functionNode.body);
|
|
65395
|
-
return functionCfgs.get(functionNode) ?? null;
|
|
65396
|
-
};
|
|
65397
|
-
const enclosingFunction = (node) => {
|
|
65398
|
-
let current = node;
|
|
65399
|
-
while (current) {
|
|
65400
|
-
if (isFunctionLike$2(current)) return current;
|
|
65401
|
-
if (isNodeOfType(current, "Program")) return current;
|
|
65402
|
-
current = current.parent ?? null;
|
|
65403
|
-
}
|
|
65404
|
-
return null;
|
|
65405
|
-
};
|
|
65406
|
-
const cfgFor = (functionLike) => {
|
|
65407
|
-
return getFunctionEntry(functionLike)?.cfg ?? null;
|
|
65408
|
-
};
|
|
65409
|
-
const isUnconditionalFromEntry = (node) => {
|
|
65410
|
-
const owner = enclosingFunction(node);
|
|
65411
|
-
if (!owner) return true;
|
|
65412
|
-
const entry = getFunctionEntry(owner);
|
|
65413
|
-
if (!entry) return true;
|
|
65414
|
-
const block = entry.cfg.blockOf(node);
|
|
65415
|
-
if (!block) return true;
|
|
65416
|
-
return entry.unconditionalSet.has(block);
|
|
65417
|
-
};
|
|
65418
|
-
return {
|
|
65419
|
-
cfgFor,
|
|
65420
|
-
enclosingFunction,
|
|
65421
|
-
isUnconditionalFromEntry
|
|
65422
|
-
};
|
|
65423
|
-
};
|
|
65424
|
-
//#endregion
|
|
65425
66290
|
//#region src/plugin/utils/wrap-with-semantic-context.ts
|
|
65426
66291
|
const buildFallbackScopes = () => ({
|
|
65427
66292
|
rootScope: {
|
|
@@ -65623,6 +66488,7 @@ const REACT_COMPILER_RULES = toRuleMap(collectExternalRulesBySource("react-compi
|
|
|
65623
66488
|
//#region src/plugin/constants/cross-file-rule-ids.ts
|
|
65624
66489
|
const CROSS_FILE_RULE_IDS = new Set([
|
|
65625
66490
|
"client-passive-event-listeners",
|
|
66491
|
+
"exhaustive-deps",
|
|
65626
66492
|
"no-barrel-import",
|
|
65627
66493
|
"nextjs-missing-metadata",
|
|
65628
66494
|
"nextjs-no-use-search-params-without-suspense",
|
|
@@ -65636,11 +66502,13 @@ const CROSS_FILE_RULE_IDS = new Set([
|
|
|
65636
66502
|
"no-derived-state",
|
|
65637
66503
|
"no-derived-state-effect",
|
|
65638
66504
|
"no-event-handler",
|
|
66505
|
+
"no-effect-with-fresh-deps",
|
|
65639
66506
|
"no-initialize-state",
|
|
65640
66507
|
"no-mutating-reducer-state",
|
|
65641
66508
|
"no-unguarded-browser-global-in-render-or-hook-init",
|
|
65642
66509
|
"prefer-dynamic-import",
|
|
65643
66510
|
"rendering-hydration-mismatch-time",
|
|
66511
|
+
"rerender-memo-with-default-value",
|
|
65644
66512
|
"rn-no-legacy-shadow-styles",
|
|
65645
66513
|
"rn-no-raw-text",
|
|
65646
66514
|
"rn-prefer-expo-image",
|
|
@@ -65693,6 +66561,46 @@ const collectNextjsSearchParamsDependencies = ({ absoluteFilePath, staticImports
|
|
|
65693
66561
|
const collectEffectValueHelperDependencies = ({ absoluteFilePath, staticImports }) => {
|
|
65694
66562
|
for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
|
|
65695
66563
|
};
|
|
66564
|
+
const flattenProgramImportEntries = (program) => {
|
|
66565
|
+
const entries = [];
|
|
66566
|
+
for (const statement of program.body ?? []) {
|
|
66567
|
+
if (statement.type !== "ImportDeclaration") continue;
|
|
66568
|
+
const source = statement.source?.value;
|
|
66569
|
+
if (typeof source !== "string") continue;
|
|
66570
|
+
for (const specifier of statement.specifiers ?? []) if (specifier.type === "ImportDefaultSpecifier") entries.push({
|
|
66571
|
+
source,
|
|
66572
|
+
exportedName: "default"
|
|
66573
|
+
});
|
|
66574
|
+
else if (specifier.type === "ImportSpecifier") {
|
|
66575
|
+
const imported = specifier.imported;
|
|
66576
|
+
let exportedName = null;
|
|
66577
|
+
if (typeof imported?.name === "string") exportedName = imported.name;
|
|
66578
|
+
else if (typeof imported?.value === "string") exportedName = imported.value;
|
|
66579
|
+
if (exportedName) entries.push({
|
|
66580
|
+
source,
|
|
66581
|
+
exportedName
|
|
66582
|
+
});
|
|
66583
|
+
}
|
|
66584
|
+
}
|
|
66585
|
+
return entries;
|
|
66586
|
+
};
|
|
66587
|
+
const collectForwardedHookDependencies = ({ absoluteFilePath, staticImports }) => {
|
|
66588
|
+
const greatestTraversedDepthByFilePath = /* @__PURE__ */ new Map();
|
|
66589
|
+
const collectProgramDependencies = (filePath, program, remainingDepth) => {
|
|
66590
|
+
if ((greatestTraversedDepthByFilePath.get(filePath) ?? -1) >= remainingDepth) return;
|
|
66591
|
+
greatestTraversedDepthByFilePath.set(filePath, remainingDepth);
|
|
66592
|
+
for (const entry of flattenProgramImportEntries(program)) {
|
|
66593
|
+
const resolved = resolveCrossFileValueExportWithFilePath(filePath, entry.source, entry.exportedName);
|
|
66594
|
+
if (!resolved || remainingDepth === 0) continue;
|
|
66595
|
+
collectProgramDependencies(resolved.filePath, resolved.programNode, remainingDepth - 1);
|
|
66596
|
+
}
|
|
66597
|
+
};
|
|
66598
|
+
for (const entry of flattenImportEntries(staticImports)) {
|
|
66599
|
+
const resolved = resolveCrossFileValueExportWithFilePath(absoluteFilePath, entry.source, entry.exportedName);
|
|
66600
|
+
if (!resolved) continue;
|
|
66601
|
+
collectProgramDependencies(resolved.filePath, resolved.programNode, 4);
|
|
66602
|
+
}
|
|
66603
|
+
};
|
|
65696
66604
|
const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
|
|
65697
66605
|
const namedUseReducerLocals = /* @__PURE__ */ new Set();
|
|
65698
66606
|
const reactObjectLocals = /* @__PURE__ */ new Set();
|
|
@@ -65771,6 +66679,7 @@ const collectLegacyArchDependencies = ({ absoluteFilePath }) => {
|
|
|
65771
66679
|
};
|
|
65772
66680
|
const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
|
|
65773
66681
|
["client-passive-event-listeners", collectEffectValueHelperDependencies],
|
|
66682
|
+
["exhaustive-deps", collectForwardedHookDependencies],
|
|
65774
66683
|
["no-barrel-import", collectNoBarrelImportDependencies],
|
|
65775
66684
|
["nextjs-missing-metadata", collectNextjsMissingMetadataDependencies],
|
|
65776
66685
|
["nextjs-no-use-search-params-without-suspense", collectNextjsSearchParamsDependencies],
|
|
@@ -65784,11 +66693,13 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
|
|
|
65784
66693
|
["no-derived-state", collectEffectValueHelperDependencies],
|
|
65785
66694
|
["no-derived-state-effect", collectEffectValueHelperDependencies],
|
|
65786
66695
|
["no-event-handler", collectEffectValueHelperDependencies],
|
|
66696
|
+
["no-effect-with-fresh-deps", collectForwardedHookDependencies],
|
|
65787
66697
|
["no-initialize-state", collectEffectValueHelperDependencies],
|
|
65788
66698
|
["no-mutating-reducer-state", collectMutatingReducerDependencies],
|
|
65789
66699
|
["no-unguarded-browser-global-in-render-or-hook-init", collectNearestManifestDependencies],
|
|
65790
66700
|
["prefer-dynamic-import", collectNearestManifestDependencies],
|
|
65791
66701
|
["rendering-hydration-mismatch-time", collectNearestManifestDependencies],
|
|
66702
|
+
["rerender-memo-with-default-value", collectForwardedHookDependencies],
|
|
65792
66703
|
["rn-no-legacy-shadow-styles", collectLegacyArchDependencies],
|
|
65793
66704
|
["rn-no-raw-text", collectRnNoRawTextDependencies],
|
|
65794
66705
|
["rn-prefer-expo-image", collectNearestManifestDependencies],
|