oxlint-plugin-react-doctor 0.9.2-dev.5dc936e → 0.9.2-dev.811a2ff
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 +207 -1911
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -665,6 +665,19 @@ const TRIVIAL_INITIALIZER_NAMES = new Set([
|
|
|
665
665
|
"parseInt",
|
|
666
666
|
"parseFloat"
|
|
667
667
|
]);
|
|
668
|
+
const TRIVIAL_CONSTRUCTOR_NAMES = new Set([
|
|
669
|
+
"Date",
|
|
670
|
+
"Map",
|
|
671
|
+
"Set",
|
|
672
|
+
"WeakMap",
|
|
673
|
+
"WeakSet",
|
|
674
|
+
"WeakRef",
|
|
675
|
+
"RegExp",
|
|
676
|
+
"Error",
|
|
677
|
+
"URL",
|
|
678
|
+
"URLSearchParams",
|
|
679
|
+
"AbortController"
|
|
680
|
+
]);
|
|
668
681
|
const SETTER_PATTERN = /^set[A-Z]/;
|
|
669
682
|
const RENDER_FUNCTION_PATTERN = /^render[A-Z]/;
|
|
670
683
|
const UPPERCASE_PATTERN = /^[A-Z]/;
|
|
@@ -5874,7 +5887,6 @@ const isInlineFunctionExpression = (node) => Boolean(node && (isNodeOfType(node,
|
|
|
5874
5887
|
//#endregion
|
|
5875
5888
|
//#region src/plugin/rules/js-performance/async-await-in-loop.ts
|
|
5876
5889
|
const LOOP_STATEMENT_TYPES$1 = new Set(LOOP_TYPES);
|
|
5877
|
-
const ORDERED_OUTPUT_INSERTION_METHOD_NAMES = new Set(["push", "unshift"]);
|
|
5878
5890
|
const findFirstAwaitOutsideNestedFunctions = (block, skipNestedLoops = false) => {
|
|
5879
5891
|
let firstAwait = null;
|
|
5880
5892
|
walkAst(block, (child) => {
|
|
@@ -5941,151 +5953,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
|
|
|
5941
5953
|
});
|
|
5942
5954
|
return isWaitLike;
|
|
5943
5955
|
};
|
|
5944
|
-
const
|
|
5945
|
-
let current = node;
|
|
5946
|
-
while (isNodeOfType(current, "MemberExpression")) current = current.object;
|
|
5947
|
-
return isNodeOfType(current, "Identifier") ? current.name : null;
|
|
5948
|
-
};
|
|
5949
|
-
const isScopeWithinFunction = (candidateScope, functionScope) => {
|
|
5950
|
-
let currentScope = candidateScope;
|
|
5951
|
-
while (currentScope) {
|
|
5952
|
-
if (currentScope === functionScope) return true;
|
|
5953
|
-
currentScope = currentScope.parent;
|
|
5954
|
-
}
|
|
5955
|
-
return false;
|
|
5956
|
-
};
|
|
5957
|
-
const isSymbolDirectlyReturned = (symbol, callerFunction) => Boolean(callerFunction) && symbol.references.some((reference) => {
|
|
5958
|
-
const expressionRoot = findTransparentExpressionRoot(reference.identifier);
|
|
5959
|
-
const parent = expressionRoot.parent;
|
|
5960
|
-
return isNodeOfType(parent, "ReturnStatement") && parent.argument === expressionRoot && findEnclosingFunction$1(parent) === callerFunction;
|
|
5961
|
-
});
|
|
5962
|
-
const collectPatternBindingSymbolIds = (pattern, scopes, target) => {
|
|
5963
|
-
if (isNodeOfType(pattern, "Identifier")) {
|
|
5964
|
-
const symbol = scopes.symbolFor(pattern);
|
|
5965
|
-
if (symbol) target.add(symbol.id);
|
|
5966
|
-
return;
|
|
5967
|
-
}
|
|
5968
|
-
if (isNodeOfType(pattern, "ObjectPattern")) {
|
|
5969
|
-
for (const property of pattern.properties ?? []) if (isNodeOfType(property, "Property") && property.value) collectPatternBindingSymbolIds(property.value, scopes, target);
|
|
5970
|
-
else if (isNodeOfType(property, "RestElement") && property.argument) collectPatternBindingSymbolIds(property.argument, scopes, target);
|
|
5971
|
-
return;
|
|
5972
|
-
}
|
|
5973
|
-
if (isNodeOfType(pattern, "ArrayPattern")) {
|
|
5974
|
-
for (const element of pattern.elements ?? []) if (element) collectPatternBindingSymbolIds(element, scopes, target);
|
|
5975
|
-
return;
|
|
5976
|
-
}
|
|
5977
|
-
if (isNodeOfType(pattern, "AssignmentPattern") && pattern.left) collectPatternBindingSymbolIds(pattern.left, scopes, target);
|
|
5978
|
-
};
|
|
5979
|
-
const collectReferencedSymbolIds = (expression, scopes) => {
|
|
5980
|
-
const referencedSymbolIds = /* @__PURE__ */ new Set();
|
|
5981
|
-
walkAst(expression, (child) => {
|
|
5982
|
-
if (child !== expression && isFunctionLike$1(child)) return false;
|
|
5983
|
-
if (!isNodeOfType(child, "Identifier")) return;
|
|
5984
|
-
const symbol = scopes.symbolFor(child);
|
|
5985
|
-
if (symbol) referencedSymbolIds.add(symbol.id);
|
|
5986
|
-
});
|
|
5987
|
-
return referencedSymbolIds;
|
|
5988
|
-
};
|
|
5989
|
-
const collectAwaitDerivedSymbolIds = (block, scopes) => {
|
|
5990
|
-
const awaitDerivedSymbolIds = /* @__PURE__ */ new Set();
|
|
5991
|
-
const bindingDependencies = [];
|
|
5992
|
-
walkAst(block, (child) => {
|
|
5993
|
-
if (child !== block && isFunctionLike$1(child)) return false;
|
|
5994
|
-
if (isNodeOfType(child, "VariableDeclarator") && child.id && child.init) {
|
|
5995
|
-
const declaredSymbolIds = /* @__PURE__ */ new Set();
|
|
5996
|
-
collectPatternBindingSymbolIds(child.id, scopes, declaredSymbolIds);
|
|
5997
|
-
if (containsDirectAwait(child.init)) for (const symbolId of declaredSymbolIds) awaitDerivedSymbolIds.add(symbolId);
|
|
5998
|
-
const referencedSymbolIds = collectReferencedSymbolIds(child.init, scopes);
|
|
5999
|
-
for (const declaredSymbolId of declaredSymbolIds) bindingDependencies.push({
|
|
6000
|
-
declaredSymbolId,
|
|
6001
|
-
referencedSymbolIds
|
|
6002
|
-
});
|
|
6003
|
-
return;
|
|
6004
|
-
}
|
|
6005
|
-
if (isNodeOfType(child, "AssignmentExpression") && child.left) {
|
|
6006
|
-
const assignedSymbolIds = /* @__PURE__ */ new Set();
|
|
6007
|
-
collectPatternBindingSymbolIds(child.left, scopes, assignedSymbolIds);
|
|
6008
|
-
if (containsDirectAwait(child.right)) for (const symbolId of assignedSymbolIds) awaitDerivedSymbolIds.add(symbolId);
|
|
6009
|
-
const referencedSymbolIds = collectReferencedSymbolIds(child.right, scopes);
|
|
6010
|
-
for (const assignedSymbolId of assignedSymbolIds) bindingDependencies.push({
|
|
6011
|
-
declaredSymbolId: assignedSymbolId,
|
|
6012
|
-
referencedSymbolIds
|
|
6013
|
-
});
|
|
6014
|
-
}
|
|
6015
|
-
});
|
|
6016
|
-
let didGrow = true;
|
|
6017
|
-
while (didGrow) {
|
|
6018
|
-
didGrow = false;
|
|
6019
|
-
for (const { declaredSymbolId, referencedSymbolIds } of bindingDependencies) {
|
|
6020
|
-
if (awaitDerivedSymbolIds.has(declaredSymbolId)) continue;
|
|
6021
|
-
for (const referencedSymbolId of referencedSymbolIds) {
|
|
6022
|
-
if (!awaitDerivedSymbolIds.has(referencedSymbolId)) continue;
|
|
6023
|
-
awaitDerivedSymbolIds.add(declaredSymbolId);
|
|
6024
|
-
didGrow = true;
|
|
6025
|
-
break;
|
|
6026
|
-
}
|
|
6027
|
-
}
|
|
6028
|
-
}
|
|
6029
|
-
return awaitDerivedSymbolIds;
|
|
6030
|
-
};
|
|
6031
|
-
const getSimpleParameterIdentifier = (parameter) => {
|
|
6032
|
-
if (isNodeOfType(parameter, "Identifier")) return parameter;
|
|
6033
|
-
if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
|
|
6034
|
-
return null;
|
|
6035
|
-
};
|
|
6036
|
-
const doesAwaitedLocalCallInsertAwaitDerivedOutput = (awaitNode, context) => {
|
|
6037
|
-
if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
|
|
6038
|
-
const callExpression = awaitNode.argument;
|
|
6039
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return false;
|
|
6040
|
-
const localFunction = resolveStaticLocalCallFunction(callExpression, context.scopes);
|
|
6041
|
-
if (!isFunctionLike$1(localFunction)) return false;
|
|
6042
|
-
const callerFunction = findEnclosingFunction$1(callExpression);
|
|
6043
|
-
const functionScope = context.scopes.ownScopeFor(localFunction);
|
|
6044
|
-
if (!functionScope) return false;
|
|
6045
|
-
const awaitDerivedSymbolIds = collectAwaitDerivedSymbolIds(localFunction.body, context.scopes);
|
|
6046
|
-
const externallyReachableParameterSymbolIds = /* @__PURE__ */ new Set();
|
|
6047
|
-
for (const [parameterIndex, parameter] of localFunction.params.entries()) {
|
|
6048
|
-
const parameterIdentifier = getSimpleParameterIdentifier(parameter);
|
|
6049
|
-
if (!parameterIdentifier) continue;
|
|
6050
|
-
const argument = callExpression.arguments[parameterIndex];
|
|
6051
|
-
if (!isNodeOfType(argument, "Identifier")) continue;
|
|
6052
|
-
const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
|
|
6053
|
-
const argumentSymbol = context.scopes.symbolFor(argument);
|
|
6054
|
-
if (parameterSymbol && argumentSymbol && (isSymbolDirectlyReturned(argumentSymbol, callerFunction) || argumentSymbol.id === parameterSymbol.id)) externallyReachableParameterSymbolIds.add(parameterSymbol.id);
|
|
6055
|
-
}
|
|
6056
|
-
let doesInsertAwaitDerivedOutput = false;
|
|
6057
|
-
walkAst(localFunction.body, (child) => {
|
|
6058
|
-
if (doesInsertAwaitDerivedOutput) return false;
|
|
6059
|
-
if (child !== localFunction.body && isFunctionLike$1(child)) return false;
|
|
6060
|
-
if (!isNodeOfType(child, "CallExpression")) return;
|
|
6061
|
-
const callee = child.callee;
|
|
6062
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !ORDERED_OUTPUT_INSERTION_METHOD_NAMES.has(callee.property.name)) return;
|
|
6063
|
-
let doesMutationConsumeAwaitedValue = false;
|
|
6064
|
-
for (const mutationArgument of child.arguments ?? []) {
|
|
6065
|
-
if (containsDirectAwait(mutationArgument)) {
|
|
6066
|
-
doesMutationConsumeAwaitedValue = true;
|
|
6067
|
-
break;
|
|
6068
|
-
}
|
|
6069
|
-
const referencedSymbolIds = collectReferencedSymbolIds(mutationArgument, context.scopes);
|
|
6070
|
-
for (const referencedSymbolId of referencedSymbolIds) if (awaitDerivedSymbolIds.has(referencedSymbolId)) {
|
|
6071
|
-
doesMutationConsumeAwaitedValue = true;
|
|
6072
|
-
break;
|
|
6073
|
-
}
|
|
6074
|
-
if (doesMutationConsumeAwaitedValue) break;
|
|
6075
|
-
}
|
|
6076
|
-
if (!doesMutationConsumeAwaitedValue) return;
|
|
6077
|
-
let receiverIdentifier = callee.object;
|
|
6078
|
-
while (isNodeOfType(receiverIdentifier, "MemberExpression")) receiverIdentifier = receiverIdentifier.object;
|
|
6079
|
-
if (!isNodeOfType(receiverIdentifier, "Identifier")) return;
|
|
6080
|
-
const receiverSymbol = context.scopes.symbolFor(receiverIdentifier);
|
|
6081
|
-
if (receiverSymbol && (externallyReachableParameterSymbolIds.has(receiverSymbol.id) || !isScopeWithinFunction(receiverSymbol.scope, functionScope) && isSymbolDirectlyReturned(receiverSymbol, callerFunction))) {
|
|
6082
|
-
doesInsertAwaitDerivedOutput = true;
|
|
6083
|
-
return false;
|
|
6084
|
-
}
|
|
6085
|
-
});
|
|
6086
|
-
return doesInsertAwaitDerivedOutput;
|
|
6087
|
-
};
|
|
6088
|
-
const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode) || doesAwaitedLocalCallInsertAwaitDerivedOutput(awaitNode, context);
|
|
5956
|
+
const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
|
|
6089
5957
|
const collectPatternIdentifiers = (pattern, target) => {
|
|
6090
5958
|
if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
|
|
6091
5959
|
else if (isNodeOfType(pattern, "ObjectPattern")) {
|
|
@@ -6231,6 +6099,11 @@ const loopBodyHasAwaitDependentEarlyExit = (block, loopLabelName) => {
|
|
|
6231
6099
|
});
|
|
6232
6100
|
return hasAwaitDependentExit;
|
|
6233
6101
|
};
|
|
6102
|
+
const getRootObjectIdentifierName = (node) => {
|
|
6103
|
+
let current = node;
|
|
6104
|
+
while (isNodeOfType(current, "MemberExpression")) current = current.object;
|
|
6105
|
+
return isNodeOfType(current, "Identifier") ? current.name : null;
|
|
6106
|
+
};
|
|
6234
6107
|
const MUTATING_ARRAY_METHOD_NAMES$2 = new Set([
|
|
6235
6108
|
...ARRAY_MUTATION_METHOD_NAMES,
|
|
6236
6109
|
"pop",
|
|
@@ -17999,14 +17872,6 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
|
|
|
17999
17872
|
//#region src/plugin/utils/is-event-handler-attribute.ts
|
|
18000
17873
|
const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && /^on[A-Z]/.test(node.name.name);
|
|
18001
17874
|
//#endregion
|
|
18002
|
-
//#region src/plugin/utils/is-early-exit-statement.ts
|
|
18003
|
-
const isEarlyExitStatement$1 = (statement) => {
|
|
18004
|
-
if (!statement) return false;
|
|
18005
|
-
if (statementAlwaysExits$1(statement)) return true;
|
|
18006
|
-
if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
|
|
18007
|
-
return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
|
|
18008
|
-
};
|
|
18009
|
-
//#endregion
|
|
18010
17875
|
//#region src/plugin/utils/is-ast-descendant.ts
|
|
18011
17876
|
/**
|
|
18012
17877
|
* True when `inner` is `outer` itself or any descendant in the AST
|
|
@@ -18225,15 +18090,13 @@ const isSynchronousIteratorCall = (callNode, callbackArgument, scopes) => {
|
|
|
18225
18090
|
}
|
|
18226
18091
|
return Boolean(methodName && EAGER_ITERATOR_METHOD_NAMES.has(methodName) && callNode.arguments[0] === callbackArgument && !isProvablyEmptyEagerCollection(callee.object, scopes) && (methodName === "forEach" ? isProvablyEagerForEachCollection(callee.object, scopes) : isProvablyEagerCollection(callee.object, scopes)));
|
|
18227
18092
|
};
|
|
18228
|
-
const isSynchronousIteratorCallbackCall = (callNode, callbackArgument) => {
|
|
18229
|
-
const callee = stripParenExpression(callNode.callee);
|
|
18230
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
|
|
18231
|
-
if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments[1] === callbackArgument;
|
|
18232
|
-
return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments[0] === callbackArgument;
|
|
18233
|
-
};
|
|
18234
18093
|
const isSynchronousIteratorCallback = (functionNode) => {
|
|
18235
18094
|
const callNode = functionNode.parent;
|
|
18236
|
-
|
|
18095
|
+
if (!isNodeOfType(callNode, "CallExpression")) return false;
|
|
18096
|
+
const callee = stripParenExpression(callNode.callee);
|
|
18097
|
+
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
|
|
18098
|
+
if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments[1] === functionNode;
|
|
18099
|
+
return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments[0] === functionNode;
|
|
18237
18100
|
};
|
|
18238
18101
|
//#endregion
|
|
18239
18102
|
//#region src/plugin/utils/is-within-assignment-target.ts
|
|
@@ -18353,35 +18216,11 @@ const resolveEventListenerCaptureValueIdentityKey = (expression, context) => {
|
|
|
18353
18216
|
const rightIdentityKey = resolveEventListenerCaptureValueIdentityKey(unwrappedExpression.right, context);
|
|
18354
18217
|
return leftIdentityKey && rightIdentityKey ? `${unwrappedExpression.type}:${unwrappedExpression.operator}:${leftIdentityKey}:${rightIdentityKey}` : null;
|
|
18355
18218
|
};
|
|
18356
|
-
const resolveReadOnlyEventListenerOptions = (optionsNode, context) => {
|
|
18357
|
-
const unwrappedOptions = stripParenExpression(optionsNode);
|
|
18358
|
-
if (!isNodeOfType(unwrappedOptions, "Identifier")) return resolveStableValue(unwrappedOptions, context);
|
|
18359
|
-
const optionsSymbol = context.scopes.symbolFor(unwrappedOptions);
|
|
18360
|
-
const initializer = optionsSymbol?.initializer ? stripParenExpression(optionsSymbol.initializer) : null;
|
|
18361
|
-
if (!optionsSymbol || !initializer) return resolveStableValue(unwrappedOptions, context);
|
|
18362
|
-
if (!isNodeOfType(initializer, "ObjectExpression")) {
|
|
18363
|
-
if (isNodeOfType(initializer, "Identifier") || isNodeOfType(initializer, "MemberExpression")) return null;
|
|
18364
|
-
return resolveStableValue(unwrappedOptions, context);
|
|
18365
|
-
}
|
|
18366
|
-
if (optionsSymbol.kind !== "const") return null;
|
|
18367
|
-
return optionsSymbol.references.every((reference) => {
|
|
18368
|
-
if (reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier)) return false;
|
|
18369
|
-
const referenceRoot = findTransparentExpressionRoot(reference.identifier);
|
|
18370
|
-
const callNode = referenceRoot.parent;
|
|
18371
|
-
if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments[2] !== referenceRoot) return false;
|
|
18372
|
-
const callee = stripParenExpression(callNode.callee);
|
|
18373
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
18374
|
-
const methodName = getStaticPropertyKeyName(callee);
|
|
18375
|
-
return methodName === "addEventListener" || methodName === "removeEventListener";
|
|
18376
|
-
}) ? initializer : null;
|
|
18377
|
-
};
|
|
18378
18219
|
const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
|
|
18379
|
-
const
|
|
18380
|
-
if (optionsNode && !stableOptionsNode) return null;
|
|
18381
|
-
const capture = resolveEventListenerCapture(stableOptionsNode, { allowIndeterminateEntries: true });
|
|
18220
|
+
const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
|
|
18382
18221
|
if (capture !== null) return `capture:${String(capture)}`;
|
|
18383
|
-
if (!
|
|
18384
|
-
const unwrappedOptions = stripParenExpression(
|
|
18222
|
+
if (!optionsNode) return null;
|
|
18223
|
+
const unwrappedOptions = stripParenExpression(optionsNode);
|
|
18385
18224
|
if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
|
|
18386
18225
|
const optionsKey = allowOpaqueOptionsIdentity ? resolveEventListenerCaptureValueIdentityKey(unwrappedOptions, context) : null;
|
|
18387
18226
|
return optionsKey ? `options:${optionsKey}` : null;
|
|
@@ -18428,8 +18267,12 @@ const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, conte
|
|
|
18428
18267
|
return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
|
|
18429
18268
|
};
|
|
18430
18269
|
const findAssignedResourceKey = (resourceNode, context) => {
|
|
18431
|
-
|
|
18432
|
-
|
|
18270
|
+
let currentNode = resourceNode;
|
|
18271
|
+
let parentNode = currentNode.parent;
|
|
18272
|
+
while (isNodeOfType(parentNode, "ChainExpression")) {
|
|
18273
|
+
currentNode = parentNode;
|
|
18274
|
+
parentNode = currentNode.parent;
|
|
18275
|
+
}
|
|
18433
18276
|
if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode) return resolveExpressionKey(parentNode.id, context);
|
|
18434
18277
|
if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
|
|
18435
18278
|
return null;
|
|
@@ -18700,18 +18543,6 @@ const resolveIteratorCollectionKey = (expression, context) => {
|
|
|
18700
18543
|
}
|
|
18701
18544
|
return null;
|
|
18702
18545
|
};
|
|
18703
|
-
const resolveReceiverIteratorCollectionKey = (expression, context) => {
|
|
18704
|
-
if (!expression) return null;
|
|
18705
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
18706
|
-
if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
|
|
18707
|
-
const collectionExpression = findForOfStatementForIteratorExpression(unwrappedExpression, context)?.right;
|
|
18708
|
-
if (!collectionExpression) return null;
|
|
18709
|
-
const collectionIdentifier = stripParenExpression(collectionExpression);
|
|
18710
|
-
if (!isNodeOfType(collectionIdentifier, "Identifier") || !isPrivatePlainConstIdentifier(collectionIdentifier, context)) return null;
|
|
18711
|
-
const collectionSymbol = context.scopes.symbolFor(collectionIdentifier);
|
|
18712
|
-
const initializer = collectionSymbol?.initializer ? stripParenExpression(collectionSymbol.initializer) : null;
|
|
18713
|
-
return collectionSymbol && isNodeOfType(initializer, "ArrayExpression") && hasOnlyReplayableCollectionReferences(collectionIdentifier, context, /* @__PURE__ */ new Set()) ? `symbol:${collectionSymbol.id}` : null;
|
|
18714
|
-
};
|
|
18715
18546
|
const isStableLoopReceiver = (expression, context) => {
|
|
18716
18547
|
if (!expression) return false;
|
|
18717
18548
|
const unwrappedExpression = stripParenExpression(expression);
|
|
@@ -19482,28 +19313,6 @@ const isFunctionReturnedFromReactHook = (functionNode, context, requireRefProper
|
|
|
19482
19313
|
});
|
|
19483
19314
|
};
|
|
19484
19315
|
const isFunctionUsedAsReactRef = (functionNode, context) => isFunctionForwardedToReactRef(functionNode, context) || isFunctionReturnedFromReactHook(functionNode, context, true);
|
|
19485
|
-
const findCallbackRefReplacementReleaseGuard = (releaseCall, ownerFunction, releaseReceiverKey, registrationReceiverKey, context) => {
|
|
19486
|
-
let descendant = releaseCall;
|
|
19487
|
-
let ancestor = descendant.parent;
|
|
19488
|
-
while (ancestor && ancestor !== ownerFunction) {
|
|
19489
|
-
if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant && ancestor.alternate === null) {
|
|
19490
|
-
const test = stripParenExpression(ancestor.test);
|
|
19491
|
-
if (!isNodeOfType(test, "LogicalExpression") || test.operator !== "&&") return null;
|
|
19492
|
-
const operands = [stripParenExpression(test.left), stripParenExpression(test.right)];
|
|
19493
|
-
const hasLiveReceiverTest = operands.some((operand) => doesTestRequireLiveExpressionKey(operand, releaseReceiverKey, context));
|
|
19494
|
-
const hasDifferentReceiverTest = operands.some((operand) => {
|
|
19495
|
-
if (!isNodeOfType(operand, "BinaryExpression") || operand.operator !== "!==" && operand.operator !== "!=") return false;
|
|
19496
|
-
const leftKey = resolveExpressionKey(operand.left, context);
|
|
19497
|
-
const rightKey = resolveExpressionKey(operand.right, context);
|
|
19498
|
-
return leftKey === releaseReceiverKey && rightKey === registrationReceiverKey || rightKey === releaseReceiverKey && leftKey === registrationReceiverKey;
|
|
19499
|
-
});
|
|
19500
|
-
return hasLiveReceiverTest && hasDifferentReceiverTest ? ancestor : null;
|
|
19501
|
-
}
|
|
19502
|
-
descendant = ancestor;
|
|
19503
|
-
ancestor = descendant.parent;
|
|
19504
|
-
}
|
|
19505
|
-
return null;
|
|
19506
|
-
};
|
|
19507
19316
|
const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
|
|
19508
19317
|
if (!isNodeOfType(usage.node, "CallExpression")) return false;
|
|
19509
19318
|
const usageFunction = findEnclosingFunction$1(usage.node);
|
|
@@ -19524,7 +19333,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
|
|
|
19524
19333
|
if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
|
|
19525
19334
|
if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveReactRefSymbol(stripParenExpression(child.left), context.scopes)?.id === releaseRefSymbol.id && resolveExpressionKey(child.right, context) === registrationReceiverKey && releaseStart !== null && (getRangeStart(child) ?? -1) > releaseStart) matchingOwnershipAssignments.push(child);
|
|
19526
19335
|
});
|
|
19527
|
-
const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ??
|
|
19336
|
+
const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
|
|
19528
19337
|
const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
|
|
19529
19338
|
return doNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
|
|
19530
19339
|
};
|
|
@@ -19674,21 +19483,14 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
19674
19483
|
if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
|
|
19675
19484
|
if (usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName))) return true;
|
|
19676
19485
|
if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
|
|
19486
|
+
if (usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1) return isProvenLegacyMediaQueryListMethodCall(usage.node, "addListener", context) && releaseVerbName === "removeListener" && isProvenLegacyMediaQueryListMethodCall(callNode, "removeListener", context) && usage.receiverKey !== null && resolveStableMediaQueryListenerIdentityKey(callee.object, context) === usage.receiverKey && usage.handlerKey !== null && resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context) === usage.handlerKey;
|
|
19677
19487
|
if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
|
|
19678
|
-
if (usage.registrationVerbName === "addListener" && releaseVerbName === "removeListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1) {
|
|
19679
|
-
if (callNode.arguments?.length !== 1) return false;
|
|
19680
|
-
const registrationHandler = resolveStableValue(usage.node.arguments[0], context);
|
|
19681
|
-
if (!isProvenLegacyMediaQueryListMethodCall(usage.node, "addListener", context) && !isFunctionLike$1(registrationHandler)) return false;
|
|
19682
|
-
}
|
|
19683
19488
|
if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
|
|
19684
19489
|
if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
|
|
19685
19490
|
if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
|
|
19686
19491
|
}
|
|
19687
19492
|
if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
|
|
19688
|
-
|
|
19689
|
-
const registrationReceiverCollectionKey = isNodeOfType(registrationCallee, "MemberExpression") ? resolveReceiverIteratorCollectionKey(registrationCallee.object, context) : null;
|
|
19690
|
-
const releaseReceiverCollectionKeyForPair = resolveReceiverIteratorCollectionKey(callee.object, context);
|
|
19691
|
-
if (!(registrationReceiverCollectionKey !== null && registrationReceiverCollectionKey === releaseReceiverCollectionKeyForPair) && (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey)) return false;
|
|
19493
|
+
if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
|
|
19692
19494
|
if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
|
|
19693
19495
|
const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
|
|
19694
19496
|
if (!pairedVerbNames || !matchesPairedReleaseVerb(releaseVerbName, pairedVerbNames)) return false;
|
|
@@ -19725,7 +19527,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
19725
19527
|
const usesUnaryListenerSignatureForCalls = isNodeOfType(usage.node, "CallExpression") && usesUnaryListenerSignature(usage.node, callNode);
|
|
19726
19528
|
const releaseHandler = usesUnaryListenerSignatureForCalls ? callNode.arguments?.[0] : callNode.arguments?.[1];
|
|
19727
19529
|
if (!releaseHandler) return releaseVerbName === "off";
|
|
19728
|
-
const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.
|
|
19530
|
+
const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.eventKey : usage.handlerKey;
|
|
19729
19531
|
const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignatureForCalls ? 0 : 1] : null;
|
|
19730
19532
|
return expectedHandlerKey !== null && resolveResourceIdentityKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
|
|
19731
19533
|
}
|
|
@@ -20199,7 +20001,6 @@ const findRetainedFunctionLeak = (retainedFunction, context, options) => {
|
|
|
20199
20001
|
walkAst(body, (child) => {
|
|
20200
20002
|
if (leak !== null) return false;
|
|
20201
20003
|
if (isFunctionLike$1(child)) return false;
|
|
20202
|
-
if (!isNodeReachableWithinFunction(child, context)) return false;
|
|
20203
20004
|
if (isSocketConstruction(child) && !doesResourceResultEscape(child, allowReturnedSocketEscape, false, context)) {
|
|
20204
20005
|
const socketUsage = {
|
|
20205
20006
|
kind: "socket",
|
|
@@ -20452,164 +20253,6 @@ const isInlineRetainedHandlerFunction = (functionNode, context) => {
|
|
|
20452
20253
|
const objectParent = objectExpression.parent;
|
|
20453
20254
|
return (isNodeOfType(objectParent, "CallExpression") && objectParent.arguments.some((argument) => argument === objectExpression) || isNodeOfType(objectParent, "JSXExpressionContainer")) && findRenderPhaseComponentOrHook(parentNode, context.scopes) !== null;
|
|
20454
20255
|
};
|
|
20455
|
-
const readInvocationArgumentValue = (expression, context) => {
|
|
20456
|
-
if (!expression) return {
|
|
20457
|
-
isDefinitelyUndefined: true,
|
|
20458
|
-
truthiness: "falsy"
|
|
20459
|
-
};
|
|
20460
|
-
const target = stripParenExpression(expression);
|
|
20461
|
-
if (isNodeOfType(target, "Literal")) return {
|
|
20462
|
-
isDefinitelyUndefined: false,
|
|
20463
|
-
truthiness: target.value ? "truthy" : "falsy"
|
|
20464
|
-
};
|
|
20465
|
-
if (isNodeOfType(target, "Identifier") && target.name === "undefined" && context.scopes.isGlobalReference(target)) return {
|
|
20466
|
-
isDefinitelyUndefined: true,
|
|
20467
|
-
truthiness: "falsy"
|
|
20468
|
-
};
|
|
20469
|
-
if (isNodeOfType(target, "UnaryExpression") && target.operator === "void") return {
|
|
20470
|
-
isDefinitelyUndefined: true,
|
|
20471
|
-
truthiness: "falsy"
|
|
20472
|
-
};
|
|
20473
|
-
if (isNodeOfType(target, "ArrayExpression") || isNodeOfType(target, "ArrowFunctionExpression") || isNodeOfType(target, "ClassExpression") || isNodeOfType(target, "FunctionExpression") || isNodeOfType(target, "NewExpression") || isNodeOfType(target, "ObjectExpression")) return {
|
|
20474
|
-
isDefinitelyUndefined: false,
|
|
20475
|
-
truthiness: "truthy"
|
|
20476
|
-
};
|
|
20477
|
-
return {
|
|
20478
|
-
isDefinitelyUndefined: false,
|
|
20479
|
-
truthiness: "unknown"
|
|
20480
|
-
};
|
|
20481
|
-
};
|
|
20482
|
-
const readInvocationConditionTruthiness = (expression, parameterValues, context) => {
|
|
20483
|
-
const target = stripParenExpression(expression);
|
|
20484
|
-
const atomicValue = readInvocationArgumentValue(target, context);
|
|
20485
|
-
if (atomicValue.truthiness !== "unknown") return atomicValue.truthiness;
|
|
20486
|
-
if (isNodeOfType(target, "Identifier")) {
|
|
20487
|
-
const symbol = context.scopes.symbolFor(target);
|
|
20488
|
-
return symbol ? parameterValues.get(symbol.id)?.truthiness ?? "unknown" : "unknown";
|
|
20489
|
-
}
|
|
20490
|
-
if (isNodeOfType(target, "UnaryExpression") && target.operator === "!") {
|
|
20491
|
-
const argumentTruthiness = readInvocationConditionTruthiness(target.argument, parameterValues, context);
|
|
20492
|
-
return argumentTruthiness === "truthy" ? "falsy" : argumentTruthiness === "falsy" ? "truthy" : "unknown";
|
|
20493
|
-
}
|
|
20494
|
-
if (isNodeOfType(target, "LogicalExpression")) {
|
|
20495
|
-
const leftTruthiness = readInvocationConditionTruthiness(target.left, parameterValues, context);
|
|
20496
|
-
const rightTruthiness = readInvocationConditionTruthiness(target.right, parameterValues, context);
|
|
20497
|
-
if (target.operator === "&&") {
|
|
20498
|
-
if (leftTruthiness === "falsy" || rightTruthiness === "falsy") return "falsy";
|
|
20499
|
-
return leftTruthiness === "truthy" && rightTruthiness === "truthy" ? "truthy" : "unknown";
|
|
20500
|
-
}
|
|
20501
|
-
if (target.operator === "||") {
|
|
20502
|
-
if (leftTruthiness === "truthy" || rightTruthiness === "truthy") return "truthy";
|
|
20503
|
-
return leftTruthiness === "falsy" && rightTruthiness === "falsy" ? "falsy" : "unknown";
|
|
20504
|
-
}
|
|
20505
|
-
return "unknown";
|
|
20506
|
-
}
|
|
20507
|
-
if (isNodeOfType(target, "ConditionalExpression")) {
|
|
20508
|
-
const testTruthiness = readInvocationConditionTruthiness(target.test, parameterValues, context);
|
|
20509
|
-
if (testTruthiness === "truthy") return readInvocationConditionTruthiness(target.consequent, parameterValues, context);
|
|
20510
|
-
if (testTruthiness === "falsy") return readInvocationConditionTruthiness(target.alternate, parameterValues, context);
|
|
20511
|
-
const consequentTruthiness = readInvocationConditionTruthiness(target.consequent, parameterValues, context);
|
|
20512
|
-
return consequentTruthiness === readInvocationConditionTruthiness(target.alternate, parameterValues, context) ? consequentTruthiness : "unknown";
|
|
20513
|
-
}
|
|
20514
|
-
if (isNodeOfType(target, "CallExpression") && isNodeOfType(target.callee, "Identifier") && target.callee.name === "Boolean" && context.scopes.isGlobalReference(target.callee) && target.arguments[0] && isAstNode(target.arguments[0])) return readInvocationConditionTruthiness(target.arguments[0], parameterValues, context);
|
|
20515
|
-
return "unknown";
|
|
20516
|
-
};
|
|
20517
|
-
const getInvocationParameterValues = (retainedFunction, invocation, leakNode, context) => {
|
|
20518
|
-
const parameterValues = /* @__PURE__ */ new Map();
|
|
20519
|
-
if (!isFunctionLike$1(retainedFunction) || !invocation.isDirect) return parameterValues;
|
|
20520
|
-
for (const [parameterIndex, parameter] of retainedFunction.params.entries()) {
|
|
20521
|
-
const argument = invocation.call.arguments[parameterIndex];
|
|
20522
|
-
const argumentExpression = argument && isAstNode(argument) ? argument : null;
|
|
20523
|
-
let parameterIdentifier = null;
|
|
20524
|
-
let parameterValue = readInvocationArgumentValue(argumentExpression, context);
|
|
20525
|
-
if (isNodeOfType(parameter, "Identifier")) parameterIdentifier = parameter;
|
|
20526
|
-
else if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) {
|
|
20527
|
-
parameterIdentifier = parameter.left;
|
|
20528
|
-
if (parameterValue.isDefinitelyUndefined) parameterValue = readInvocationArgumentValue(parameter.right, context);
|
|
20529
|
-
} else if (isNodeOfType(parameter, "RestElement") && isNodeOfType(parameter.argument, "Identifier")) {
|
|
20530
|
-
parameterIdentifier = parameter.argument;
|
|
20531
|
-
parameterValue = {
|
|
20532
|
-
isDefinitelyUndefined: false,
|
|
20533
|
-
truthiness: "truthy"
|
|
20534
|
-
};
|
|
20535
|
-
}
|
|
20536
|
-
if (!parameterIdentifier) continue;
|
|
20537
|
-
const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
|
|
20538
|
-
if (!parameterSymbol) continue;
|
|
20539
|
-
const isWrittenBeforeLeak = parameterSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] < leakNode.range[0]);
|
|
20540
|
-
parameterValues.set(parameterSymbol.id, isWrittenBeforeLeak ? {
|
|
20541
|
-
isDefinitelyUndefined: false,
|
|
20542
|
-
truthiness: "unknown"
|
|
20543
|
-
} : parameterValue);
|
|
20544
|
-
}
|
|
20545
|
-
return parameterValues;
|
|
20546
|
-
};
|
|
20547
|
-
const isLeakPathDisabledForInvocation = (retainedFunction, leakNode, invocation, context) => {
|
|
20548
|
-
if (!invocation.isDirect) return false;
|
|
20549
|
-
const parameterValues = getInvocationParameterValues(retainedFunction, invocation, leakNode, context);
|
|
20550
|
-
let child = leakNode;
|
|
20551
|
-
let ancestor = leakNode.parent ?? null;
|
|
20552
|
-
while (ancestor && ancestor !== retainedFunction) {
|
|
20553
|
-
if (isNodeOfType(ancestor, "BlockStatement")) {
|
|
20554
|
-
const childIndex = ancestor.body.findIndex((statement) => statement === child);
|
|
20555
|
-
for (const precedingStatement of ancestor.body.slice(0, childIndex)) {
|
|
20556
|
-
if (!isNodeOfType(precedingStatement, "IfStatement") || precedingStatement.alternate || !isEarlyExitStatement$1(precedingStatement.consequent)) continue;
|
|
20557
|
-
if (readInvocationConditionTruthiness(precedingStatement.test, parameterValues, context) === "truthy") return true;
|
|
20558
|
-
}
|
|
20559
|
-
}
|
|
20560
|
-
let requiredTruthiness = null;
|
|
20561
|
-
let condition = null;
|
|
20562
|
-
if (isNodeOfType(ancestor, "IfStatement")) {
|
|
20563
|
-
condition = ancestor.test;
|
|
20564
|
-
requiredTruthiness = ancestor.consequent === child ? "truthy" : "falsy";
|
|
20565
|
-
} else if (isNodeOfType(ancestor, "ConditionalExpression")) {
|
|
20566
|
-
condition = ancestor.test;
|
|
20567
|
-
requiredTruthiness = ancestor.consequent === child ? "truthy" : "falsy";
|
|
20568
|
-
} else if (isNodeOfType(ancestor, "LogicalExpression") && ancestor.right === child && ancestor.operator !== "??") {
|
|
20569
|
-
condition = ancestor.left;
|
|
20570
|
-
requiredTruthiness = ancestor.operator === "&&" ? "truthy" : "falsy";
|
|
20571
|
-
} else if ((isNodeOfType(ancestor, "WhileStatement") || isNodeOfType(ancestor, "DoWhileStatement")) && ancestor.body === child) {
|
|
20572
|
-
condition = ancestor.test;
|
|
20573
|
-
requiredTruthiness = "truthy";
|
|
20574
|
-
} else if (isNodeOfType(ancestor, "ForStatement") && ancestor.body === child && ancestor.test) {
|
|
20575
|
-
condition = ancestor.test;
|
|
20576
|
-
requiredTruthiness = "truthy";
|
|
20577
|
-
}
|
|
20578
|
-
if (condition && requiredTruthiness) {
|
|
20579
|
-
const conditionTruthiness = readInvocationConditionTruthiness(condition, parameterValues, context);
|
|
20580
|
-
if (conditionTruthiness !== "unknown" && conditionTruthiness !== requiredTruthiness) return true;
|
|
20581
|
-
}
|
|
20582
|
-
child = ancestor;
|
|
20583
|
-
ancestor = ancestor.parent ?? null;
|
|
20584
|
-
}
|
|
20585
|
-
return false;
|
|
20586
|
-
};
|
|
20587
|
-
const getEffectRetainedInvocations = (retainedFunction, context) => {
|
|
20588
|
-
if (!isFunctionLike$1(retainedFunction)) return [];
|
|
20589
|
-
const componentFunction = findEnclosingFunction$1(retainedFunction);
|
|
20590
|
-
if (!componentFunction || !isFunctionLike$1(componentFunction)) return [];
|
|
20591
|
-
const invocations = [];
|
|
20592
|
-
walkAst(componentFunction.body, (child) => {
|
|
20593
|
-
if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction || !isReactHookCall(child, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return;
|
|
20594
|
-
const effectCallback = getEffectCallback(child);
|
|
20595
|
-
if (!effectCallback || !isFunctionLike$1(effectCallback)) return;
|
|
20596
|
-
walkAst(effectCallback.body, (effectChild) => {
|
|
20597
|
-
if (effectChild !== effectCallback.body && isFunctionLike$1(effectChild)) return false;
|
|
20598
|
-
if (!isNodeOfType(effectChild, "CallExpression") || !isNodeReachableWithinFunction(effectChild, context)) return;
|
|
20599
|
-
const isDirectInvocation = resolveRefOwnedCleanupFunction(effectChild.callee, context) === retainedFunction;
|
|
20600
|
-
const isSynchronousIteratorInvocation = effectChild.arguments.some((argument) => isAstNode(argument) && resolveRefOwnedCleanupFunction(argument, context) === retainedFunction && isSynchronousIteratorCallbackCall(effectChild, argument));
|
|
20601
|
-
if (isDirectInvocation) invocations.push({
|
|
20602
|
-
call: effectChild,
|
|
20603
|
-
isDirect: true
|
|
20604
|
-
});
|
|
20605
|
-
if (isSynchronousIteratorInvocation) invocations.push({
|
|
20606
|
-
call: effectChild,
|
|
20607
|
-
isDirect: false
|
|
20608
|
-
});
|
|
20609
|
-
});
|
|
20610
|
-
});
|
|
20611
|
-
return invocations;
|
|
20612
|
-
};
|
|
20613
20256
|
const effectNeedsCleanup = defineRule({
|
|
20614
20257
|
id: "effect-needs-cleanup",
|
|
20615
20258
|
title: "Effect subscription or timer never cleaned up",
|
|
@@ -20620,19 +20263,13 @@ const effectNeedsCleanup = defineRule({
|
|
|
20620
20263
|
const reportRetainedLeak = (retainedFunction) => {
|
|
20621
20264
|
const refEffectUsage = getReactRefEffectUsage(retainedFunction, context);
|
|
20622
20265
|
if (!refEffectUsage && !isPotentiallyReachableFunction(retainedFunction, context)) return;
|
|
20623
|
-
const effectInvocations = getEffectRetainedInvocations(retainedFunction, context);
|
|
20624
|
-
const isEffectInvoked = effectInvocations.length > 0;
|
|
20625
20266
|
const leak = findRetainedFunctionLeak(retainedFunction, context, refEffectUsage ? {
|
|
20626
20267
|
allowReturnedResourceEscape: refEffectUsage.doesEffectOwnEveryResult,
|
|
20627
20268
|
allowReturnedTimerEscape: false,
|
|
20628
20269
|
includeOneShotTimers: true,
|
|
20629
20270
|
requireCallableReturnedResource: true
|
|
20630
|
-
} : isEffectInvoked ? {
|
|
20631
|
-
allowReturnedTimerEscape: false,
|
|
20632
|
-
includeOneShotTimers: true
|
|
20633
20271
|
} : void 0);
|
|
20634
20272
|
if (!leak) return;
|
|
20635
|
-
if (isEffectInvoked && leak.resourceName === "setTimeout" && (!isNodeReachableWithinFunction(leak.node, context) || isFunctionLike$1(retainedFunction) && retainedFunction.params.length > 0 && !context.cfg.isUnconditionalFromEntry(leak.node) && effectInvocations.every((invocation) => isLeakPathDisabledForInvocation(retainedFunction, leak.node, invocation, context)))) return;
|
|
20636
20273
|
const resourceNoun = RESOURCE_NOUN_BY_KIND[leak.kind];
|
|
20637
20274
|
context.report({
|
|
20638
20275
|
node: leak.node,
|
|
@@ -25052,14 +24689,14 @@ const getFirstLegendChild = (children, targetNode) => {
|
|
|
25052
24689
|
if (isNodeOfType(child, "JSXExpressionContainer")) {
|
|
25053
24690
|
const potentialLegends = [];
|
|
25054
24691
|
collectPotentialLegends(child.expression, potentialLegends);
|
|
25055
|
-
const containingLegend = potentialLegends.find((legend) => isDescendantOf
|
|
24692
|
+
const containingLegend = potentialLegends.find((legend) => isDescendantOf(targetNode, legend));
|
|
25056
24693
|
if (containingLegend) return containingLegend;
|
|
25057
24694
|
if (potentialLegends[0]) return potentialLegends[0];
|
|
25058
24695
|
}
|
|
25059
24696
|
}
|
|
25060
24697
|
return null;
|
|
25061
24698
|
};
|
|
25062
|
-
const isDescendantOf
|
|
24699
|
+
const isDescendantOf = (node, ancestor) => {
|
|
25063
24700
|
let current = node.parent;
|
|
25064
24701
|
while (current) {
|
|
25065
24702
|
if (current === ancestor) return true;
|
|
@@ -25084,7 +24721,7 @@ const isDisabledByFieldsetAncestor = (node, context) => {
|
|
|
25084
24721
|
while (ancestor) {
|
|
25085
24722
|
if (isNodeOfType(ancestor, "JSXElement") && resolveJsxElementType(ancestor.openingElement) === "fieldset" && openingElementMayBeDisabled(ancestor.openingElement, context)) {
|
|
25086
24723
|
const firstLegend = getFirstLegendChild(ancestor.children, node);
|
|
25087
|
-
if (!firstLegend || !isDescendantOf
|
|
24724
|
+
if (!firstLegend || !isDescendantOf(node, firstLegend)) return true;
|
|
25088
24725
|
}
|
|
25089
24726
|
ancestor = ancestor.parent;
|
|
25090
24727
|
}
|
|
@@ -29461,11 +29098,6 @@ const jsCacheStorage = defineRule({
|
|
|
29461
29098
|
});
|
|
29462
29099
|
//#endregion
|
|
29463
29100
|
//#region src/plugin/rules/js-performance/js-combine-iterations.ts
|
|
29464
|
-
const SMALL_ARRAY_NON_MUTATING_METHODS = new Set([
|
|
29465
|
-
...CHAINABLE_ITERATION_METHODS,
|
|
29466
|
-
"find",
|
|
29467
|
-
"some"
|
|
29468
|
-
]);
|
|
29469
29101
|
const isIteratorProducingCall = (callExpression, generatorNamesInFile) => {
|
|
29470
29102
|
const callee = callExpression.callee;
|
|
29471
29103
|
if (isNodeOfType(callee, "MemberExpression")) {
|
|
@@ -29577,34 +29209,21 @@ const isStringSplitRootedChain = (receiverNode) => {
|
|
|
29577
29209
|
return false;
|
|
29578
29210
|
};
|
|
29579
29211
|
const isSmallLiteralArray = (node) => {
|
|
29580
|
-
|
|
29581
|
-
|
|
29582
|
-
|
|
29583
|
-
if (elements.length === 0 || elements.length > 9) return false;
|
|
29212
|
+
if (!isNodeOfType(node, "ArrayExpression")) return false;
|
|
29213
|
+
const elements = node.elements ?? [];
|
|
29214
|
+
if (elements.length === 0 || elements.length > 8) return false;
|
|
29584
29215
|
for (const element of elements) {
|
|
29585
29216
|
if (!element) continue;
|
|
29586
29217
|
if (isNodeOfType(element, "SpreadElement")) return false;
|
|
29587
29218
|
}
|
|
29588
29219
|
return true;
|
|
29589
29220
|
};
|
|
29590
|
-
const
|
|
29591
|
-
const identifierRoot = findTransparentExpressionRoot(identifier);
|
|
29592
|
-
const memberExpression = identifierRoot.parent;
|
|
29593
|
-
if (!isNodeOfType(memberExpression, "MemberExpression") || memberExpression.object !== identifierRoot || !isNodeOfType(memberExpression.property, "Identifier") || !SMALL_ARRAY_NON_MUTATING_METHODS.has(memberExpression.property.name)) return false;
|
|
29594
|
-
const callExpression = memberExpression.parent;
|
|
29595
|
-
return isNodeOfType(callExpression, "CallExpression") && callExpression.callee === memberExpression;
|
|
29596
|
-
};
|
|
29597
|
-
const isSmallLiteralArrayRootedChain = (receiverNode, scopes) => {
|
|
29221
|
+
const isSmallLiteralArrayRootedChain = (receiverNode, smallConstArrayNames) => {
|
|
29598
29222
|
let cursor = receiverNode;
|
|
29599
29223
|
while (cursor) {
|
|
29600
29224
|
cursor = stripParenExpression(cursor);
|
|
29601
29225
|
if (isNodeOfType(cursor, "ArrayExpression")) return isSmallLiteralArray(cursor);
|
|
29602
|
-
if (isNodeOfType(cursor, "Identifier"))
|
|
29603
|
-
const symbol = scopes.symbolFor(cursor);
|
|
29604
|
-
if (!symbol?.initializer || !isSmallLiteralArray(symbol.initializer)) return false;
|
|
29605
|
-
if (!isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "Identifier")) return false;
|
|
29606
|
-
return (symbol.kind === "const" || symbol.kind === "let" || symbol.kind === "var") && symbol.references.every((reference) => reference.flag === "read" && isNonMutatingSmallArrayMethodReference(reference.identifier));
|
|
29607
|
-
}
|
|
29226
|
+
if (isNodeOfType(cursor, "Identifier")) return smallConstArrayNames.has(cursor.name);
|
|
29608
29227
|
if (!isNodeOfType(cursor, "CallExpression")) return false;
|
|
29609
29228
|
if (!isChainPassThroughCall(cursor)) return false;
|
|
29610
29229
|
const nextCallee = cursor.callee;
|
|
@@ -29613,6 +29232,22 @@ const isSmallLiteralArrayRootedChain = (receiverNode, scopes) => {
|
|
|
29613
29232
|
}
|
|
29614
29233
|
return false;
|
|
29615
29234
|
};
|
|
29235
|
+
const collectSmallConstArrayNames = (programNode) => {
|
|
29236
|
+
const names = /* @__PURE__ */ new Set();
|
|
29237
|
+
const statements = programNode.body ?? [];
|
|
29238
|
+
for (const statement of statements) {
|
|
29239
|
+
const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
|
|
29240
|
+
if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) continue;
|
|
29241
|
+
if (declaration.kind !== "const") continue;
|
|
29242
|
+
for (const declarator of declaration.declarations ?? []) {
|
|
29243
|
+
if (!isNodeOfType(declarator, "VariableDeclarator")) continue;
|
|
29244
|
+
if (!isNodeOfType(declarator.id, "Identifier")) continue;
|
|
29245
|
+
if (!declarator.init || !isSmallLiteralArray(declarator.init)) continue;
|
|
29246
|
+
names.add(declarator.id.name);
|
|
29247
|
+
}
|
|
29248
|
+
}
|
|
29249
|
+
return names;
|
|
29250
|
+
};
|
|
29616
29251
|
const collectGeneratorNames = (programNode) => {
|
|
29617
29252
|
const generatorNames = /* @__PURE__ */ new Set();
|
|
29618
29253
|
walkAst(programNode, (child) => {
|
|
@@ -29633,11 +29268,16 @@ const jsCombineIterations = defineRule({
|
|
|
29633
29268
|
create: (context) => {
|
|
29634
29269
|
let programNode = null;
|
|
29635
29270
|
let generatorNamesInFile = null;
|
|
29271
|
+
let smallConstArrayNames = null;
|
|
29636
29272
|
const coveredChainCalls = /* @__PURE__ */ new WeakSet();
|
|
29637
29273
|
const getGeneratorNamesInFile = () => {
|
|
29638
29274
|
generatorNamesInFile ??= programNode ? collectGeneratorNames(programNode) : /* @__PURE__ */ new Set();
|
|
29639
29275
|
return generatorNamesInFile;
|
|
29640
29276
|
};
|
|
29277
|
+
const getSmallConstArrayNames = () => {
|
|
29278
|
+
smallConstArrayNames ??= programNode ? collectSmallConstArrayNames(programNode) : /* @__PURE__ */ new Set();
|
|
29279
|
+
return smallConstArrayNames;
|
|
29280
|
+
};
|
|
29641
29281
|
return {
|
|
29642
29282
|
Program(node) {
|
|
29643
29283
|
programNode = node;
|
|
@@ -29667,7 +29307,7 @@ const jsCombineIterations = defineRule({
|
|
|
29667
29307
|
if (isTypePredicateArrow(filterArgument)) return;
|
|
29668
29308
|
}
|
|
29669
29309
|
if (isReceiverChainIteratorRooted(innerCall.callee.object, getGeneratorNamesInFile())) return;
|
|
29670
|
-
if (isSmallLiteralArrayRootedChain(innerCall.callee.object,
|
|
29310
|
+
if (isSmallLiteralArrayRootedChain(innerCall.callee.object, getSmallConstArrayNames())) return;
|
|
29671
29311
|
if (isStringSplitRootedChain(innerCall.callee.object)) return;
|
|
29672
29312
|
coveredChainCalls.add(innerCall);
|
|
29673
29313
|
context.report({
|
|
@@ -45600,6 +45240,14 @@ const noAriaInvalidWithoutDescription = defineRule({
|
|
|
45600
45240
|
} })
|
|
45601
45241
|
});
|
|
45602
45242
|
//#endregion
|
|
45243
|
+
//#region src/plugin/utils/is-early-exit-statement.ts
|
|
45244
|
+
const isEarlyExitStatement$1 = (statement) => {
|
|
45245
|
+
if (!statement) return false;
|
|
45246
|
+
if (statementAlwaysExits$1(statement)) return true;
|
|
45247
|
+
if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
|
|
45248
|
+
return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
|
|
45249
|
+
};
|
|
45250
|
+
//#endregion
|
|
45603
45251
|
//#region src/plugin/utils/unwrap-negative-guard-form.ts
|
|
45604
45252
|
const unwrapNegativeGuardForm = (test) => {
|
|
45605
45253
|
const expression = stripParenExpression(test);
|
|
@@ -56087,13 +55735,9 @@ const isInitialOnlyPropName = (propName) => {
|
|
|
56087
55735
|
return /^initial[A-Z]/.test(propName) || /^default[A-Z]/.test(propName) || /^seed[A-Z]/.test(propName) || /^starting[A-Z]/.test(propName) || /^baseline[A-Z]/.test(propName) || /^preset[A-Z]/.test(propName);
|
|
56088
55736
|
};
|
|
56089
55737
|
//#endregion
|
|
56090
|
-
//#region src/plugin/utils/nextjs-page-data-export-names.ts
|
|
56091
|
-
const NEXTJS_PAGE_DATA_EXPORT_NAMES = new Set(["getServerSideProps", "getStaticProps"]);
|
|
56092
|
-
//#endregion
|
|
56093
55738
|
//#region src/plugin/rules/state-and-effects/no-derived-use-state.ts
|
|
56094
55739
|
const isInitialOnlySeedName = (propName) => isInitialOnlyPropName(propName) || propName === "initial" || propName === "autoFocus" || propName === "autoPlay" || propName === "startOpen" || /^initially[A-Z]/.test(propName) || /Initial([A-Z]|$)/.test(propName);
|
|
56095
55740
|
const SNAPSHOT_STATE_NAME_PATTERN = /^(initial|previous|prev|preserved|saved|original|cached|snapshot|prior|debounced|deferred)([A-Z_]|$)/;
|
|
56096
|
-
const INTERNAL_STATE_NAME_PATTERN = /^(internal|uncontrolled)([A-Z_]|$)/;
|
|
56097
55741
|
const getStateSetterName = (useStateCall) => {
|
|
56098
55742
|
const declarator = useStateCall.parent;
|
|
56099
55743
|
if (!isNodeOfType(declarator, "VariableDeclarator")) return null;
|
|
@@ -56247,6 +55891,7 @@ const isDraftCommittedToParent = (componentFunction, stateValueName, isPropName)
|
|
|
56247
55891
|
});
|
|
56248
55892
|
return isCommitted;
|
|
56249
55893
|
};
|
|
55894
|
+
const NEXTJS_PAGE_DATA_EXPORT_NAMES = new Set(["getServerSideProps", "getStaticProps"]);
|
|
56250
55895
|
const isNextjsDataFetchingPage = (node) => {
|
|
56251
55896
|
const program = findProgramRoot(node);
|
|
56252
55897
|
if (!program) return false;
|
|
@@ -56290,48 +55935,6 @@ const isInRenderScope = (node, componentFunction) => {
|
|
|
56290
55935
|
}
|
|
56291
55936
|
return true;
|
|
56292
55937
|
};
|
|
56293
|
-
const isReferenceToBinding = (reference, bindingIdentifier, context) => {
|
|
56294
|
-
if (!isNodeOfType(reference, "Identifier")) return false;
|
|
56295
|
-
const bindingSymbol = context.scopes.symbolFor(bindingIdentifier);
|
|
56296
|
-
if (!bindingSymbol) return false;
|
|
56297
|
-
return (context.scopes.referenceFor(reference)?.resolvedSymbol)?.id === bindingSymbol.id;
|
|
56298
|
-
};
|
|
56299
|
-
const isControlledPropFallbackExpression = (expression, stateBinding, isPropName, context) => {
|
|
56300
|
-
if (isNodeOfType(expression, "ConditionalExpression")) {
|
|
56301
|
-
const consequent = unwrapInitializerSeed(expression.consequent);
|
|
56302
|
-
const alternate = unwrapInitializerSeed(expression.alternate);
|
|
56303
|
-
return isReferenceToBinding(consequent, stateBinding, context) && isPropDerivedArgument(alternate, isPropName) || isPropDerivedArgument(consequent, isPropName) && isReferenceToBinding(alternate, stateBinding, context);
|
|
56304
|
-
}
|
|
56305
|
-
return isNodeOfType(expression, "LogicalExpression") && expression.operator === "??" && isPropDerivedArgument(unwrapInitializerSeed(expression.left), isPropName) && isReferenceToBinding(unwrapInitializerSeed(expression.right), stateBinding, context);
|
|
56306
|
-
};
|
|
56307
|
-
const isUserEditableControlledFallback = (useStateCall, isPropName, context) => {
|
|
56308
|
-
const declarator = useStateCall.parent;
|
|
56309
|
-
if (!isNodeOfType(declarator, "VariableDeclarator")) return false;
|
|
56310
|
-
if (!isNodeOfType(declarator.id, "ArrayPattern")) return false;
|
|
56311
|
-
const stateBinding = declarator.id.elements?.[0];
|
|
56312
|
-
const setterBinding = declarator.id.elements?.[1];
|
|
56313
|
-
if (!isNodeOfType(stateBinding, "Identifier") || !isNodeOfType(setterBinding, "Identifier")) return false;
|
|
56314
|
-
if (!INTERNAL_STATE_NAME_PATTERN.test(stateBinding.name)) return false;
|
|
56315
|
-
const componentFunction = findEnclosingFunction$1(useStateCall);
|
|
56316
|
-
if (!componentFunction) return false;
|
|
56317
|
-
let hasControlledFallback = false;
|
|
56318
|
-
let hasUserEdit = false;
|
|
56319
|
-
walkAst(componentFunction, (child) => {
|
|
56320
|
-
if (child !== componentFunction && isFunctionLike$1(child)) return false;
|
|
56321
|
-
if (!hasControlledFallback && (isNodeOfType(child, "ConditionalExpression") || isNodeOfType(child, "LogicalExpression")) && isControlledPropFallbackExpression(child, stateBinding, isPropName, context)) hasControlledFallback = true;
|
|
56322
|
-
});
|
|
56323
|
-
walkAst(componentFunction, (child) => {
|
|
56324
|
-
if (hasUserEdit) return false;
|
|
56325
|
-
if (!isNodeOfType(child, "CallExpression")) return;
|
|
56326
|
-
if (!isReferenceToBinding(child.callee, setterBinding, context)) return;
|
|
56327
|
-
if (!isHandlerShapedReseed(child, componentFunction)) return;
|
|
56328
|
-
const setterArgument = child.arguments?.[0];
|
|
56329
|
-
if (!setterArgument || isPropDerivedArgument(unwrapInitializerSeed(setterArgument), isPropName)) return;
|
|
56330
|
-
hasUserEdit = true;
|
|
56331
|
-
return false;
|
|
56332
|
-
});
|
|
56333
|
-
return hasControlledFallback && hasUserEdit;
|
|
56334
|
-
};
|
|
56335
55938
|
const getStateValueName = (useStateCall) => {
|
|
56336
55939
|
const declarator = useStateCall.parent;
|
|
56337
55940
|
if (!isNodeOfType(declarator, "VariableDeclarator")) return null;
|
|
@@ -56396,7 +55999,6 @@ const noDerivedUseState = defineRule({
|
|
|
56396
55999
|
const reportStalePropCopy = (propName) => {
|
|
56397
56000
|
if (isIntentionalSnapshotState(node)) return;
|
|
56398
56001
|
if (hasSessionDismissProp(propStackTracker.getCurrentPropNames())) return;
|
|
56399
|
-
if (isUserEditableControlledFallback(node, propStackTracker.isPropName, context)) return;
|
|
56400
56002
|
if (isDraftReseedOrRenderAdjusted(node, propStackTracker.isPropName)) return;
|
|
56401
56003
|
if (isEffectDrivenResync(node)) return;
|
|
56402
56004
|
if (isNextjsDataFetchingPage(node)) return;
|
|
@@ -64541,106 +64143,6 @@ const isGatedByFalsyInitialState = (node, scopes) => {
|
|
|
64541
64143
|
};
|
|
64542
64144
|
//#endregion
|
|
64543
64145
|
//#region src/plugin/rules/performance/no-hydration-branch-on-browser-global.ts
|
|
64544
|
-
const findGuardingIfStatements = (node, functionBoundary) => {
|
|
64545
|
-
const guardingIfStatements = [];
|
|
64546
|
-
let currentNode = node.parent;
|
|
64547
|
-
while (currentNode && currentNode !== functionBoundary) {
|
|
64548
|
-
if (isNodeOfType(currentNode, "IfStatement")) guardingIfStatements.push(currentNode);
|
|
64549
|
-
currentNode = currentNode.parent;
|
|
64550
|
-
}
|
|
64551
|
-
return guardingIfStatements;
|
|
64552
|
-
};
|
|
64553
|
-
const doesNodeReadSymbol = (node, symbol) => {
|
|
64554
|
-
let doesReadSymbol = false;
|
|
64555
|
-
walkAst(node, (childNode) => {
|
|
64556
|
-
if (isNodeOfType(childNode, "Identifier") && symbol.references.some((reference) => reference.identifier === childNode && reference.flag !== "write")) {
|
|
64557
|
-
doesReadSymbol = true;
|
|
64558
|
-
return false;
|
|
64559
|
-
}
|
|
64560
|
-
});
|
|
64561
|
-
return doesReadSymbol;
|
|
64562
|
-
};
|
|
64563
|
-
const collectWrittenSymbols = (node, scopes) => {
|
|
64564
|
-
const writtenSymbols = /* @__PURE__ */ new Set();
|
|
64565
|
-
walkAst(node, (childNode) => {
|
|
64566
|
-
if (childNode !== node && isFunctionLike$1(childNode)) return false;
|
|
64567
|
-
if (!isNodeOfType(childNode, "Identifier")) return;
|
|
64568
|
-
const reference = scopes.referenceFor(childNode);
|
|
64569
|
-
if (!reference || reference.flag === "read" || !reference.resolvedSymbol) return;
|
|
64570
|
-
writtenSymbols.add(reference.resolvedSymbol);
|
|
64571
|
-
});
|
|
64572
|
-
return writtenSymbols;
|
|
64573
|
-
};
|
|
64574
|
-
const isDescendantOf = (node, ancestorNode) => {
|
|
64575
|
-
let currentNode = node.parent;
|
|
64576
|
-
while (currentNode) {
|
|
64577
|
-
if (currentNode === ancestorNode) return true;
|
|
64578
|
-
currentNode = currentNode.parent;
|
|
64579
|
-
}
|
|
64580
|
-
return false;
|
|
64581
|
-
};
|
|
64582
|
-
const getAssignedValue = (identifier) => {
|
|
64583
|
-
const assignmentExpression = identifier.parent;
|
|
64584
|
-
return isNodeOfType(assignmentExpression, "AssignmentExpression") && assignmentExpression.operator === "=" && assignmentExpression.left === identifier ? assignmentExpression.right : null;
|
|
64585
|
-
};
|
|
64586
|
-
const doesGuardPreserveInitialSymbolValue = (symbol, guardingIfStatement, scopes) => {
|
|
64587
|
-
const initialValue = symbol.initializer;
|
|
64588
|
-
if (!initialValue) return false;
|
|
64589
|
-
const guardedWrites = symbol.references.filter((reference) => reference.flag !== "read" && isDescendantOf(reference.identifier, guardingIfStatement));
|
|
64590
|
-
return guardedWrites.length > 0 && guardedWrites.every((reference) => {
|
|
64591
|
-
const assignedValue = getAssignedValue(reference.identifier);
|
|
64592
|
-
return Boolean(assignedValue && areExpressionsStructurallyEqual(initialValue, assignedValue) && doEquivalentExpressionBindingsMatch(initialValue, assignedValue, scopes));
|
|
64593
|
-
});
|
|
64594
|
-
};
|
|
64595
|
-
const isWriteOverwrittenBefore = (symbol, writeIdentifier, guardingIfStatement, readIdentifier, context) => symbol.references.some((reference) => reference.flag !== "read" && reference.identifier !== writeIdentifier && !isDescendantOf(reference.identifier, guardingIfStatement) && isNodeReachableWithinFunction(reference.identifier, context) && isUnconditionalOrStaticallySelected(reference.identifier, context) && getNodeStartIndex(reference.identifier) > getNodeStartIndex(writeIdentifier) && getNodeStartIndex(reference.identifier) < getNodeStartIndex(readIdentifier));
|
|
64596
|
-
const isUnconditionalOrStaticallySelected = (node, context) => {
|
|
64597
|
-
if (context.cfg.isUnconditionalFromEntry(node)) return true;
|
|
64598
|
-
let currentNode = node;
|
|
64599
|
-
let outermostStaticIfStatement = null;
|
|
64600
|
-
let parentNode = currentNode.parent;
|
|
64601
|
-
while (parentNode) {
|
|
64602
|
-
if (isFunctionLike$1(parentNode)) break;
|
|
64603
|
-
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
64604
|
-
const staticResult = readInitialStateBoolean(parentNode.test, context.scopes);
|
|
64605
|
-
let selectedBranch = null;
|
|
64606
|
-
if (staticResult === true) selectedBranch = parentNode.consequent;
|
|
64607
|
-
if (staticResult === false) selectedBranch = parentNode.alternate;
|
|
64608
|
-
if (!selectedBranch || currentNode !== selectedBranch && !isDescendantOf(currentNode, selectedBranch)) return false;
|
|
64609
|
-
outermostStaticIfStatement = parentNode;
|
|
64610
|
-
}
|
|
64611
|
-
currentNode = parentNode;
|
|
64612
|
-
parentNode = currentNode.parent;
|
|
64613
|
-
}
|
|
64614
|
-
return Boolean(outermostStaticIfStatement && context.cfg.isUnconditionalFromEntry(outermostStaticIfStatement));
|
|
64615
|
-
};
|
|
64616
|
-
const containsExplicitReactRuntimeReference = (node, scopes) => {
|
|
64617
|
-
let hasRuntimeReference = false;
|
|
64618
|
-
walkAst(node, (childNode) => {
|
|
64619
|
-
if (isNodeOfType(childNode, "ImportDeclaration") && typeof childNode.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(childNode.source.value)) {
|
|
64620
|
-
hasRuntimeReference = true;
|
|
64621
|
-
return false;
|
|
64622
|
-
}
|
|
64623
|
-
if (!isNodeOfType(childNode, "CallExpression")) return;
|
|
64624
|
-
const sourceArgument = (childNode.arguments ?? [])[0];
|
|
64625
|
-
if (!isNodeOfType(childNode.callee, "Identifier") || childNode.callee.name !== "require" || !scopes.isGlobalReference(childNode.callee) || !isNodeOfType(sourceArgument, "Literal") || typeof sourceArgument.value !== "string" || !REACT_RUNTIME_MODULE_SOURCES.has(sourceArgument.value)) return;
|
|
64626
|
-
hasRuntimeReference = true;
|
|
64627
|
-
return false;
|
|
64628
|
-
});
|
|
64629
|
-
return hasRuntimeReference;
|
|
64630
|
-
};
|
|
64631
|
-
const findComponentRenderingLocalFunctionResult = (functionNode, scopes) => {
|
|
64632
|
-
const bindingIdentifier = getDirectFunctionBindingIdentifier(functionNode);
|
|
64633
|
-
if (!isNodeOfType(bindingIdentifier, "Identifier")) return null;
|
|
64634
|
-
const functionSymbol = scopes.symbolFor(bindingIdentifier);
|
|
64635
|
-
if (!functionSymbol) return null;
|
|
64636
|
-
for (const reference of functionSymbol.references) {
|
|
64637
|
-
const callExpression = reference.identifier.parent;
|
|
64638
|
-
if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== reference.identifier) continue;
|
|
64639
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(callExpression, scopes);
|
|
64640
|
-
if (componentOrHookNode && isInRenderedOutput(callExpression, componentOrHookNode, scopes)) return componentOrHookNode;
|
|
64641
|
-
}
|
|
64642
|
-
return null;
|
|
64643
|
-
};
|
|
64644
64146
|
const evaluateEquality$1 = (operator, left, right) => {
|
|
64645
64147
|
if (operator === "===" || operator === "==") return left === right;
|
|
64646
64148
|
if (operator === "!==" || operator === "!=") return left !== right;
|
|
@@ -64691,107 +64193,6 @@ const readLogicalConditionResult = (operator, leftResult, rightResult) => {
|
|
|
64691
64193
|
if (leftResult === false && rightResult === false) return false;
|
|
64692
64194
|
return null;
|
|
64693
64195
|
};
|
|
64694
|
-
const areLooselyEqualPrimitiveResults = (left, right) => {
|
|
64695
|
-
if (left.kind === right.kind) return left.value === right.value;
|
|
64696
|
-
if (left.kind === "null" && right.kind === "undefined" || left.kind === "undefined" && right.kind === "null") return true;
|
|
64697
|
-
if (left.kind === "boolean") return areLooselyEqualPrimitiveResults({
|
|
64698
|
-
kind: "number",
|
|
64699
|
-
value: left.value ? 1 : 0
|
|
64700
|
-
}, right);
|
|
64701
|
-
if (right.kind === "boolean") return areLooselyEqualPrimitiveResults(left, {
|
|
64702
|
-
kind: "number",
|
|
64703
|
-
value: right.value ? 1 : 0
|
|
64704
|
-
});
|
|
64705
|
-
if (left.kind === "number" && right.kind === "string") return left.value === Number(right.value);
|
|
64706
|
-
if (left.kind === "string" && right.kind === "number") return Number(left.value) === right.value;
|
|
64707
|
-
return false;
|
|
64708
|
-
};
|
|
64709
|
-
const readHydrationPrimitiveResult = (expression, context, runtime, state) => {
|
|
64710
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
64711
|
-
const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
|
|
64712
|
-
if (predicateMatch) return {
|
|
64713
|
-
kind: "boolean",
|
|
64714
|
-
value: predicateMatch[`${runtime}Result`]
|
|
64715
|
-
};
|
|
64716
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) {
|
|
64717
|
-
const value = unwrappedExpression.value;
|
|
64718
|
-
if (value === null) return {
|
|
64719
|
-
kind: "null",
|
|
64720
|
-
value
|
|
64721
|
-
};
|
|
64722
|
-
if (typeof value === "boolean") return {
|
|
64723
|
-
kind: "boolean",
|
|
64724
|
-
value
|
|
64725
|
-
};
|
|
64726
|
-
if (typeof value === "number") return {
|
|
64727
|
-
kind: "number",
|
|
64728
|
-
value
|
|
64729
|
-
};
|
|
64730
|
-
if (typeof value === "string") return {
|
|
64731
|
-
kind: "string",
|
|
64732
|
-
value
|
|
64733
|
-
};
|
|
64734
|
-
return null;
|
|
64735
|
-
}
|
|
64736
|
-
if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return {
|
|
64737
|
-
kind: "undefined",
|
|
64738
|
-
value: void 0
|
|
64739
|
-
};
|
|
64740
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
64741
|
-
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
64742
|
-
const parameterValue = symbol ? state.parameterValuesBySymbolId.get(symbol.id) : null;
|
|
64743
|
-
if (symbol && parameterValue && !state.visitedSymbolIds.has(symbol.id)) {
|
|
64744
|
-
state.visitedSymbolIds.add(symbol.id);
|
|
64745
|
-
const result = readHydrationPrimitiveResult(parameterValue, context, runtime, state);
|
|
64746
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64747
|
-
return result;
|
|
64748
|
-
}
|
|
64749
|
-
if (symbol && symbol.kind === "const" && symbol.initializer && symbol.references.every((reference) => reference.flag === "read") && !state.visitedSymbolIds.has(symbol.id)) {
|
|
64750
|
-
state.visitedSymbolIds.add(symbol.id);
|
|
64751
|
-
const result = readHydrationPrimitiveResult(symbol.initializer, context, runtime, state);
|
|
64752
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64753
|
-
return result;
|
|
64754
|
-
}
|
|
64755
|
-
}
|
|
64756
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
|
|
64757
|
-
const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
|
|
64758
|
-
return argumentResult === null ? null : {
|
|
64759
|
-
kind: "boolean",
|
|
64760
|
-
value: !argumentResult
|
|
64761
|
-
};
|
|
64762
|
-
}
|
|
64763
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
64764
|
-
const leftResult = readHydrationPrimitiveResult(unwrappedExpression.left, context, runtime, state);
|
|
64765
|
-
const rightResult = readHydrationPrimitiveResult(unwrappedExpression.right, context, runtime, state);
|
|
64766
|
-
if (!leftResult || !rightResult) return null;
|
|
64767
|
-
if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
|
|
64768
|
-
const areEqual = leftResult.kind === rightResult.kind && leftResult.value === rightResult.value;
|
|
64769
|
-
return {
|
|
64770
|
-
kind: "boolean",
|
|
64771
|
-
value: unwrappedExpression.operator === "===" ? areEqual : !areEqual
|
|
64772
|
-
};
|
|
64773
|
-
}
|
|
64774
|
-
if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
|
|
64775
|
-
const areEqual = areLooselyEqualPrimitiveResults(leftResult, rightResult);
|
|
64776
|
-
return {
|
|
64777
|
-
kind: "boolean",
|
|
64778
|
-
value: unwrappedExpression.operator === "==" ? areEqual : !areEqual
|
|
64779
|
-
};
|
|
64780
|
-
}
|
|
64781
|
-
}
|
|
64782
|
-
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
64783
|
-
const callArguments = unwrappedExpression.arguments ?? [];
|
|
64784
|
-
const callee = stripParenExpression(unwrappedExpression.callee);
|
|
64785
|
-
if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) {
|
|
64786
|
-
const argumentResult = readHydrationConditionResult(callArguments[0], context, runtime, state);
|
|
64787
|
-
return argumentResult === null ? null : {
|
|
64788
|
-
kind: "boolean",
|
|
64789
|
-
value: argumentResult
|
|
64790
|
-
};
|
|
64791
|
-
}
|
|
64792
|
-
}
|
|
64793
|
-
return null;
|
|
64794
|
-
};
|
|
64795
64196
|
const readHydrationConditionResult = (expression, context, runtime, state) => {
|
|
64796
64197
|
const unwrappedExpression = stripParenExpression(expression);
|
|
64797
64198
|
const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
|
|
@@ -64840,10 +64241,6 @@ const readHydrationConditionResult = (expression, context, runtime, state) => {
|
|
|
64840
64241
|
parameterValuesBySymbolId
|
|
64841
64242
|
});
|
|
64842
64243
|
}
|
|
64843
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
64844
|
-
const result = readHydrationPrimitiveResult(unwrappedExpression, context, runtime, state);
|
|
64845
|
-
return result?.kind === "boolean" && typeof result.value === "boolean" ? result.value : null;
|
|
64846
|
-
}
|
|
64847
64244
|
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
|
|
64848
64245
|
const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
|
|
64849
64246
|
return argumentResult === null ? null : !argumentResult;
|
|
@@ -64904,19 +64301,13 @@ const doEquivalentExpressionBindingsMatch = (leftExpression, rightExpression, sc
|
|
|
64904
64301
|
const rightSymbol = scopes.symbolFor(right);
|
|
64905
64302
|
return leftSymbol || rightSymbol ? leftSymbol?.id === rightSymbol?.id : true;
|
|
64906
64303
|
}
|
|
64907
|
-
|
|
64908
|
-
|
|
64909
|
-
|
|
64910
|
-
|
|
64911
|
-
|
|
64912
|
-
|
|
64913
|
-
|
|
64914
|
-
}
|
|
64915
|
-
if (!Array.isArray(leftValue)) continue;
|
|
64916
|
-
if (!Array.isArray(rightValue)) return false;
|
|
64917
|
-
const leftNodes = leftValue.filter(isAstNode);
|
|
64918
|
-
const rightNodes = rightValue.filter(isAstNode);
|
|
64919
|
-
if (leftNodes.length !== rightNodes.length || leftNodes.some((leftNode, index) => !rightNodes[index] || !doEquivalentExpressionBindingsMatch(leftNode, rightNodes[index], scopes))) return false;
|
|
64304
|
+
if (isNodeOfType(left, "MemberExpression") && isNodeOfType(right, "MemberExpression")) return doEquivalentExpressionBindingsMatch(left.object, right.object, scopes) && (!left.computed || doEquivalentExpressionBindingsMatch(left.property, right.property, scopes));
|
|
64305
|
+
if (isNodeOfType(left, "CallExpression") && isNodeOfType(right, "CallExpression")) {
|
|
64306
|
+
const rightArguments = right.arguments ?? [];
|
|
64307
|
+
return doEquivalentExpressionBindingsMatch(left.callee, right.callee, scopes) && (left.arguments ?? []).every((argument, index) => {
|
|
64308
|
+
const rightArgument = rightArguments[index];
|
|
64309
|
+
return Boolean(rightArgument && doEquivalentExpressionBindingsMatch(argument, rightArgument, scopes));
|
|
64310
|
+
});
|
|
64920
64311
|
}
|
|
64921
64312
|
return true;
|
|
64922
64313
|
};
|
|
@@ -64930,57 +64321,6 @@ const doHelperReturnValuesDiffer = (leftValues, rightValues, context) => {
|
|
|
64930
64321
|
const everyValueHasEquivalent = (values, candidateValues) => values.every((value) => candidateValues.some((candidateValue) => areHelperReturnValuesEquivalent(value, candidateValue, context)));
|
|
64931
64322
|
return !everyValueHasEquivalent(leftValues, rightValues) || !everyValueHasEquivalent(rightValues, leftValues);
|
|
64932
64323
|
};
|
|
64933
|
-
const isExpressionProvablyReflexive = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
64934
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
64935
|
-
if (matchBrowserPredicate(unwrappedExpression, context)) return true;
|
|
64936
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) return typeof unwrappedExpression.value !== "number" || !Number.isNaN(unwrappedExpression.value);
|
|
64937
|
-
if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return true;
|
|
64938
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
64939
|
-
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
64940
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer) return false;
|
|
64941
|
-
visitedSymbolIds.add(symbol.id);
|
|
64942
|
-
const assignedValues = symbol.references.filter((reference) => reference.flag !== "read").map((reference) => getAssignedValue(reference.identifier));
|
|
64943
|
-
const isReflexive = isExpressionProvablyReflexive(symbol.initializer, context, visitedSymbolIds) && assignedValues.every((assignedValue) => Boolean(assignedValue && isExpressionProvablyReflexive(assignedValue, context, visitedSymbolIds)));
|
|
64944
|
-
visitedSymbolIds.delete(symbol.id);
|
|
64945
|
-
return isReflexive;
|
|
64946
|
-
}
|
|
64947
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isExpressionProvablyReflexive(unwrappedExpression.consequent, context, visitedSymbolIds) && isExpressionProvablyReflexive(unwrappedExpression.alternate, context, visitedSymbolIds);
|
|
64948
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && (unwrappedExpression.operator === "!" || unwrappedExpression.operator === "typeof" || unwrappedExpression.operator === "void")) return true;
|
|
64949
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) return unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==" || unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=";
|
|
64950
|
-
if (isNodeOfType(unwrappedExpression, "ArrayExpression") || isNodeOfType(unwrappedExpression, "ObjectExpression") || isNodeOfType(unwrappedExpression, "FunctionExpression") || isNodeOfType(unwrappedExpression, "ArrowFunctionExpression") || isNodeOfType(unwrappedExpression, "TemplateLiteral")) return true;
|
|
64951
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
|
|
64952
|
-
const callee = stripParenExpression(unwrappedExpression.callee);
|
|
64953
|
-
return isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee);
|
|
64954
|
-
};
|
|
64955
|
-
const getReturnedObjectPropertyValues = (node, propertyName, scopes) => {
|
|
64956
|
-
if (isNodeOfType(node, "ReturnStatement")) return node.argument ? getReturnedObjectPropertyValues(node.argument, propertyName, scopes) : [];
|
|
64957
|
-
if (isNodeOfType(node, "ObjectExpression")) return node.properties.flatMap((property) => isNodeOfType(property, "Property") && property.kind === "init" && getResolvedStaticPropertyName(property, scopes) === propertyName ? [property.value] : []);
|
|
64958
|
-
if (isNodeOfType(node, "IfStatement")) return [...getReturnedObjectPropertyValues(node.consequent, propertyName, scopes), ...node.alternate ? getReturnedObjectPropertyValues(node.alternate, propertyName, scopes) : []];
|
|
64959
|
-
if (isNodeOfType(node, "TryStatement")) return [
|
|
64960
|
-
...getReturnedObjectPropertyValues(node.block, propertyName, scopes),
|
|
64961
|
-
...node.handler ? getReturnedObjectPropertyValues(node.handler.body, propertyName, scopes) : [],
|
|
64962
|
-
...node.finalizer ? getReturnedObjectPropertyValues(node.finalizer, propertyName, scopes) : []
|
|
64963
|
-
];
|
|
64964
|
-
if (!isNodeOfType(node, "BlockStatement")) return [];
|
|
64965
|
-
const propertyValues = [];
|
|
64966
|
-
for (const childStatement of node.body) {
|
|
64967
|
-
propertyValues.push(...getReturnedObjectPropertyValues(childStatement, propertyName, scopes));
|
|
64968
|
-
if (statementAlwaysExits$1(childStatement)) break;
|
|
64969
|
-
}
|
|
64970
|
-
return propertyValues;
|
|
64971
|
-
};
|
|
64972
|
-
const matchHydrationFunctionPropertyResult = (functionNode, propertyName, context, state) => {
|
|
64973
|
-
if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
|
|
64974
|
-
state.visitedFunctionNodes.add(functionNode);
|
|
64975
|
-
const propertyValues = getReturnedObjectPropertyValues(functionNode.body, propertyName, context.scopes);
|
|
64976
|
-
let match = null;
|
|
64977
|
-
for (const propertyValue of propertyValues) {
|
|
64978
|
-
match = matchHydrationConditionInternal(propertyValue, context, state);
|
|
64979
|
-
if (match) break;
|
|
64980
|
-
}
|
|
64981
|
-
state.visitedFunctionNodes.delete(functionNode);
|
|
64982
|
-
return match;
|
|
64983
|
-
};
|
|
64984
64324
|
const matchHydrationConditionInternal = (expression, context, state) => {
|
|
64985
64325
|
const unwrappedExpression = stripParenExpression(expression);
|
|
64986
64326
|
const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
|
|
@@ -64997,90 +64337,14 @@ const matchHydrationConditionInternal = (expression, context, state) => {
|
|
|
64997
64337
|
state.visitedSymbolIds.delete(symbol.id);
|
|
64998
64338
|
return match;
|
|
64999
64339
|
}
|
|
65000
|
-
if (symbol && (symbol.kind === "let" || symbol.kind === "var") && !state.visitedSymbolIds.has(symbol.id)) {
|
|
65001
|
-
state.visitedSymbolIds.add(symbol.id);
|
|
65002
|
-
if (symbol.initializer && symbol.references.every((reference) => reference.flag === "read")) {
|
|
65003
|
-
const match = matchHydrationConditionInternal(symbol.initializer, context, state);
|
|
65004
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
65005
|
-
return match;
|
|
65006
|
-
}
|
|
65007
|
-
for (const reference of symbol.references) {
|
|
65008
|
-
if (reference.flag === "read") continue;
|
|
65009
|
-
if (!isNodeReachableWithinFunction(reference.identifier, context)) continue;
|
|
65010
|
-
const enclosingFunction = findEnclosingFunction$1(reference.identifier);
|
|
65011
|
-
if (!enclosingFunction) continue;
|
|
65012
|
-
for (const guardingIfStatement of findGuardingIfStatements(reference.identifier, enclosingFunction)) {
|
|
65013
|
-
if (doesGuardPreserveInitialSymbolValue(symbol, guardingIfStatement, context.scopes) || isWriteOverwrittenBefore(symbol, reference.identifier, guardingIfStatement, unwrappedExpression, context)) continue;
|
|
65014
|
-
const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
|
|
65015
|
-
if (match) {
|
|
65016
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
65017
|
-
return match;
|
|
65018
|
-
}
|
|
65019
|
-
}
|
|
65020
|
-
}
|
|
65021
|
-
const readingFunction = findEnclosingFunction$1(unwrappedExpression);
|
|
65022
|
-
for (const reference of symbol.references) {
|
|
65023
|
-
if (reference.flag === "read") continue;
|
|
65024
|
-
const writingFunction = findEnclosingFunction$1(reference.identifier);
|
|
65025
|
-
if (!readingFunction || !isFunctionLike$1(writingFunction) || writingFunction === readingFunction || writingFunction.async || writingFunction.params.length > 0 || isNodeOfType(writingFunction, "FunctionDeclaration") && writingFunction.generator || isNodeOfType(writingFunction, "FunctionExpression") && writingFunction.generator) continue;
|
|
65026
|
-
const assignedValue = getAssignedValue(reference.identifier);
|
|
65027
|
-
if (symbol.initializer && assignedValue && areExpressionsStructurallyEqual(symbol.initializer, assignedValue) && doEquivalentExpressionBindingsMatch(symbol.initializer, assignedValue, context.scopes)) continue;
|
|
65028
|
-
const functionBinding = getDirectFunctionBindingIdentifier(writingFunction);
|
|
65029
|
-
if (!isNodeOfType(functionBinding, "Identifier")) continue;
|
|
65030
|
-
const functionSymbol = context.scopes.symbolFor(functionBinding);
|
|
65031
|
-
if (!functionSymbol) continue;
|
|
65032
|
-
for (const functionReference of functionSymbol.references) {
|
|
65033
|
-
const callExpression = functionReference.identifier.parent;
|
|
65034
|
-
if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== functionReference.identifier || (callExpression.arguments ?? []).length > 0 || findEnclosingFunction$1(callExpression) !== readingFunction || !isNodeReachableWithinFunction(callExpression, context) || getNodeStartIndex(callExpression) >= getNodeStartIndex(unwrappedExpression)) continue;
|
|
65035
|
-
for (const guardingIfStatement of findGuardingIfStatements(callExpression, readingFunction)) {
|
|
65036
|
-
if (isWriteOverwrittenBefore(symbol, callExpression, guardingIfStatement, unwrappedExpression, context)) continue;
|
|
65037
|
-
const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
|
|
65038
|
-
if (match) {
|
|
65039
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
65040
|
-
return match;
|
|
65041
|
-
}
|
|
65042
|
-
}
|
|
65043
|
-
}
|
|
65044
|
-
}
|
|
65045
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
65046
|
-
}
|
|
65047
64340
|
if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || state.visitedSymbolIds.has(symbol.id)) return null;
|
|
65048
64341
|
state.visitedSymbolIds.add(symbol.id);
|
|
65049
64342
|
const match = matchHydrationConditionInternal(symbol.initializer, context, state);
|
|
65050
64343
|
state.visitedSymbolIds.delete(symbol.id);
|
|
65051
64344
|
return match;
|
|
65052
64345
|
}
|
|
65053
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression")) {
|
|
65054
|
-
const propertyName = getResolvedStaticPropertyName(unwrappedExpression, context.scopes, {
|
|
65055
|
-
allowConstNumericLiteral: true,
|
|
65056
|
-
stringifyNonStringLiterals: true
|
|
65057
|
-
});
|
|
65058
|
-
const object = stripParenExpression(unwrappedExpression.object);
|
|
65059
|
-
if (propertyName === null || !isNodeOfType(object, "CallExpression")) return null;
|
|
65060
|
-
const callArguments = object.arguments ?? [];
|
|
65061
|
-
if (isReactApiCall(object, "useMemo", context.scopes, {
|
|
65062
|
-
allowGlobalReactNamespace: true,
|
|
65063
|
-
resolveNamedAliases: true
|
|
65064
|
-
})) {
|
|
65065
|
-
const callbackArgument = callArguments[0];
|
|
65066
|
-
if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
|
|
65067
|
-
const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
|
|
65068
|
-
return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? matchHydrationFunctionPropertyResult(callbackFunction, propertyName, context, state) : null;
|
|
65069
|
-
}
|
|
65070
|
-
const helperFunction = resolveExactLocalFunction(object.callee, context.scopes);
|
|
65071
|
-
return isFunctionLike$1(helperFunction) && helperFunction.params.length === 0 && callArguments.length === 0 ? matchHydrationFunctionPropertyResult(helperFunction, propertyName, context, state) : null;
|
|
65072
|
-
}
|
|
65073
64346
|
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
65074
64347
|
const callArguments = unwrappedExpression.arguments ?? [];
|
|
65075
|
-
if (isReactApiCall(unwrappedExpression, "useState", context.scopes, {
|
|
65076
|
-
allowGlobalReactNamespace: true,
|
|
65077
|
-
resolveNamedAliases: true
|
|
65078
|
-
})) {
|
|
65079
|
-
const initialState = callArguments[0];
|
|
65080
|
-
if (!initialState || isNodeOfType(initialState, "SpreadElement")) return null;
|
|
65081
|
-
const lazyInitializer = resolveExactLocalFunction(initialState, context.scopes);
|
|
65082
|
-
return isFunctionLike$1(lazyInitializer) && lazyInitializer.params.length === 0 ? matchHydrationFunctionResult(lazyInitializer, context, state) : matchHydrationConditionInternal(initialState, context, state);
|
|
65083
|
-
}
|
|
65084
64348
|
if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
|
|
65085
64349
|
allowGlobalReactNamespace: true,
|
|
65086
64350
|
resolveNamedAliases: true
|
|
@@ -65108,22 +64372,6 @@ const matchHydrationConditionInternal = (expression, context, state) => {
|
|
|
65108
64372
|
});
|
|
65109
64373
|
}
|
|
65110
64374
|
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationConditionInternal(unwrappedExpression.argument, context, state);
|
|
65111
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
65112
|
-
const staticTestResult = readInitialStateBoolean(unwrappedExpression.test, context.scopes);
|
|
65113
|
-
if (staticTestResult !== null) return matchHydrationConditionInternal(staticTestResult ? unwrappedExpression.consequent : unwrappedExpression.alternate, context, state);
|
|
65114
|
-
return matchHydrationConditionInternal(unwrappedExpression.test, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.consequent, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.alternate, context, state);
|
|
65115
|
-
}
|
|
65116
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
65117
|
-
if (unwrappedExpression.operator !== "===" && unwrappedExpression.operator !== "!==" && unwrappedExpression.operator !== "==" && unwrappedExpression.operator !== "!=") return null;
|
|
65118
|
-
const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
|
|
65119
|
-
const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
|
|
65120
|
-
const nestedMatch = leftMatch ?? rightMatch;
|
|
65121
|
-
if (!nestedMatch) return null;
|
|
65122
|
-
const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client", state);
|
|
65123
|
-
const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server", state);
|
|
65124
|
-
if (clientResult !== null && serverResult !== null) return clientResult !== serverResult ? nestedMatch : null;
|
|
65125
|
-
return leftMatch && rightMatch && areExpressionsStructurallyEqual(unwrappedExpression.left, unwrappedExpression.right) && doEquivalentExpressionBindingsMatch(unwrappedExpression.left, unwrappedExpression.right, context.scopes) && isExpressionProvablyReflexive(unwrappedExpression.left, context) ? null : nestedMatch;
|
|
65126
|
-
}
|
|
65127
64375
|
if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
|
|
65128
64376
|
const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
|
|
65129
64377
|
const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
|
|
@@ -65140,13 +64388,8 @@ const matchHydrationReturningStatement = (statement, context, state) => {
|
|
|
65140
64388
|
const consequentValues = getReturnedValues(statement.consequent);
|
|
65141
64389
|
const alternateValues = statement.alternate ? getReturnedValues(statement.alternate) : findFollowingReturnedValues(statement);
|
|
65142
64390
|
if (conditionMatch && consequentValues.length > 0 && alternateValues.length > 0 && doHelperReturnValuesDiffer(consequentValues, alternateValues, context)) return conditionMatch;
|
|
65143
|
-
if (conditionMatch) {
|
|
65144
|
-
const followingReturnedValues = findFollowingReturnedValues(statement);
|
|
65145
|
-
if ([...new Set([...collectWrittenSymbols(statement.consequent, context.scopes), ...statement.alternate ? collectWrittenSymbols(statement.alternate, context.scopes) : []])].some((symbol) => !doesGuardPreserveInitialSymbolValue(symbol, statement, context.scopes) && followingReturnedValues.some((value) => doesNodeReadSymbol(value, symbol)))) return conditionMatch;
|
|
65146
|
-
}
|
|
65147
64391
|
return matchHydrationReturningStatement(statement.consequent, context, state) ?? (statement.alternate ? matchHydrationReturningStatement(statement.alternate, context, state) : null);
|
|
65148
64392
|
}
|
|
65149
|
-
if (isNodeOfType(statement, "TryStatement")) return matchHydrationReturningStatement(statement.block, context, state) ?? (statement.handler ? matchHydrationReturningStatement(statement.handler.body, context, state) : null) ?? (statement.finalizer ? matchHydrationReturningStatement(statement.finalizer, context, state) : null);
|
|
65150
64393
|
if (!isNodeOfType(statement, "BlockStatement")) return null;
|
|
65151
64394
|
for (const childStatement of statement.body) {
|
|
65152
64395
|
const match = matchHydrationReturningStatement(childStatement, context, state);
|
|
@@ -65167,62 +64410,47 @@ const matchHydrationCondition = (expression, context) => matchHydrationCondition
|
|
|
65167
64410
|
visitedFunctionNodes: /* @__PURE__ */ new Set(),
|
|
65168
64411
|
visitedSymbolIds: /* @__PURE__ */ new Set()
|
|
65169
64412
|
});
|
|
65170
|
-
const areNodeArraysEquivalent = (leftNodes, rightNodes
|
|
65171
|
-
const areRenderedBranchesEquivalent = (leftNode, rightNode
|
|
64413
|
+
const areNodeArraysEquivalent = (leftNodes, rightNodes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index]));
|
|
64414
|
+
const areRenderedBranchesEquivalent = (leftNode, rightNode) => {
|
|
65172
64415
|
if (!leftNode || !rightNode) return leftNode === rightNode;
|
|
65173
64416
|
const left = stripParenExpression(leftNode);
|
|
65174
64417
|
const right = stripParenExpression(rightNode);
|
|
65175
|
-
if (areExpressionsStructurallyEqual(left, right)) return
|
|
64418
|
+
if (areExpressionsStructurallyEqual(left, right)) return true;
|
|
65176
64419
|
if (left.type !== right.type) return false;
|
|
65177
64420
|
if (isNodeOfType(left, "JSXText") && isNodeOfType(right, "JSXText")) return left.value === right.value;
|
|
65178
64421
|
if (isNodeOfType(left, "JSXExpressionContainer") && isNodeOfType(right, "JSXExpressionContainer")) {
|
|
65179
64422
|
if (!isAstNode(left.expression) || !isAstNode(right.expression)) return left.expression.type === right.expression.type;
|
|
65180
|
-
return areRenderedBranchesEquivalent(left.expression, right.expression
|
|
64423
|
+
return areRenderedBranchesEquivalent(left.expression, right.expression);
|
|
65181
64424
|
}
|
|
65182
64425
|
if (isNodeOfType(left, "JSXElement") && isNodeOfType(right, "JSXElement")) {
|
|
65183
64426
|
if (flattenJsxName$1(left.openingElement.name) !== flattenJsxName$1(right.openingElement.name)) return false;
|
|
65184
|
-
if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes
|
|
65185
|
-
return areNodeArraysEquivalent(left.children, right.children
|
|
64427
|
+
if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes)) return false;
|
|
64428
|
+
return areNodeArraysEquivalent(left.children, right.children);
|
|
65186
64429
|
}
|
|
65187
|
-
if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children
|
|
64430
|
+
if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children);
|
|
65188
64431
|
if (isNodeOfType(left, "JSXAttribute") && isNodeOfType(right, "JSXAttribute")) {
|
|
65189
64432
|
if (flattenJsxName$1(left.name) !== flattenJsxName$1(right.name)) return false;
|
|
65190
|
-
return areRenderedBranchesEquivalent(left.value, right.value
|
|
64433
|
+
return areRenderedBranchesEquivalent(left.value, right.value);
|
|
65191
64434
|
}
|
|
65192
|
-
if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument
|
|
64435
|
+
if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument);
|
|
65193
64436
|
if (isNodeOfType(left, "TemplateLiteral") && isNodeOfType(right, "TemplateLiteral")) {
|
|
65194
64437
|
if (left.quasis.length !== right.quasis.length) return false;
|
|
65195
64438
|
if (!left.quasis.every((quasi, index) => quasi.value.cooked === right.quasis[index]?.value.cooked && quasi.value.raw === right.quasis[index]?.value.raw)) return false;
|
|
65196
|
-
return areNodeArraysEquivalent(left.expressions, right.expressions
|
|
64439
|
+
return areNodeArraysEquivalent(left.expressions, right.expressions);
|
|
65197
64440
|
}
|
|
65198
64441
|
return false;
|
|
65199
64442
|
};
|
|
65200
|
-
const
|
|
65201
|
-
if (isReactApiCall(node, "createElement", scopes, {
|
|
65202
|
-
allowGlobalReactNamespace: true,
|
|
65203
|
-
resolveNamedAliases: true
|
|
65204
|
-
})) return true;
|
|
65205
|
-
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
65206
|
-
const callee = stripParenExpression(node.callee);
|
|
65207
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "createElement") return false;
|
|
65208
|
-
const receiver = stripParenExpression(callee.object);
|
|
65209
|
-
const namespaceIdentifier = isNodeOfType(receiver, "MemberExpression") && !receiver.computed && isNodeOfType(receiver.property, "Identifier") && receiver.property.name === "default" ? stripParenExpression(receiver.object) : receiver;
|
|
65210
|
-
if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
|
|
65211
|
-
const namespaceSymbol = scopes.symbolFor(namespaceIdentifier);
|
|
65212
|
-
return Boolean(namespaceSymbol?.initializer && namespaceSymbol.references.every((reference) => reference.flag === "read") && containsExplicitReactRuntimeReference(namespaceSymbol.initializer, scopes));
|
|
65213
|
-
};
|
|
65214
|
-
const isRenderedValue = (node, scopes) => {
|
|
64443
|
+
const isRenderedValue = (node) => {
|
|
65215
64444
|
const unwrappedNode = stripParenExpression(node);
|
|
65216
64445
|
if (isNodeOfType(unwrappedNode, "Literal")) return unwrappedNode.value !== null && unwrappedNode.value !== true && unwrappedNode.value !== false && unwrappedNode.value !== "";
|
|
65217
64446
|
if (isNodeOfType(unwrappedNode, "TemplateLiteral")) return unwrappedNode.expressions.length > 0 || unwrappedNode.quasis[0]?.value.cooked !== "";
|
|
65218
|
-
if (isNodeOfType(unwrappedNode, "CallExpression")) return isProvenReactCreateElementCall(unwrappedNode, scopes);
|
|
65219
64447
|
return isNodeOfType(unwrappedNode, "JSXElement") || isNodeOfType(unwrappedNode, "JSXFragment");
|
|
65220
64448
|
};
|
|
65221
|
-
const findRenderedValueInAndBranch = (node
|
|
64449
|
+
const findRenderedValueInAndBranch = (node) => {
|
|
65222
64450
|
const unwrappedNode = stripParenExpression(node);
|
|
65223
|
-
if (
|
|
64451
|
+
if (isRenderedValue(unwrappedNode)) return unwrappedNode;
|
|
65224
64452
|
if (!isNodeOfType(unwrappedNode, "LogicalExpression") || unwrappedNode.operator !== "&&") return null;
|
|
65225
|
-
return findRenderedValueInAndBranch(unwrappedNode.right
|
|
64453
|
+
return findRenderedValueInAndBranch(unwrappedNode.right);
|
|
65226
64454
|
};
|
|
65227
64455
|
const findEnclosingJsxAttribute = (node) => {
|
|
65228
64456
|
let currentNode = node.parent;
|
|
@@ -65255,11 +64483,6 @@ const getReturnedValues = (statement) => {
|
|
|
65255
64483
|
if (!statement) return [];
|
|
65256
64484
|
if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? [statement.argument] : [];
|
|
65257
64485
|
if (isNodeOfType(statement, "IfStatement")) return [...getReturnedValues(statement.consequent), ...getReturnedValues(statement.alternate)];
|
|
65258
|
-
if (isNodeOfType(statement, "TryStatement")) return [
|
|
65259
|
-
...getReturnedValues(statement.block),
|
|
65260
|
-
...getReturnedValues(statement.handler?.body),
|
|
65261
|
-
...getReturnedValues(statement.finalizer)
|
|
65262
|
-
];
|
|
65263
64486
|
if (!isNodeOfType(statement, "BlockStatement")) return [];
|
|
65264
64487
|
const returnedValues = [];
|
|
65265
64488
|
for (const childStatement of statement.body) {
|
|
@@ -65268,127 +64491,6 @@ const getReturnedValues = (statement) => {
|
|
|
65268
64491
|
}
|
|
65269
64492
|
return returnedValues;
|
|
65270
64493
|
};
|
|
65271
|
-
const isPotentiallyRenderedValueInternal = (node, scopes, visitedFunctionNodes) => {
|
|
65272
|
-
const unwrappedNode = stripParenExpression(node);
|
|
65273
|
-
if (isRenderedValue(unwrappedNode, scopes)) return true;
|
|
65274
|
-
if (isNodeOfType(unwrappedNode, "ConditionalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.consequent, scopes, visitedFunctionNodes) && isPotentiallyRenderedValueInternal(unwrappedNode.alternate, scopes, visitedFunctionNodes);
|
|
65275
|
-
if (isNodeOfType(unwrappedNode, "LogicalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.right, scopes, visitedFunctionNodes);
|
|
65276
|
-
if (!isNodeOfType(unwrappedNode, "CallExpression")) return false;
|
|
65277
|
-
const calledFunction = resolveExactLocalFunction(unwrappedNode.callee, scopes);
|
|
65278
|
-
if (!isFunctionLike$1(calledFunction) || visitedFunctionNodes.has(calledFunction)) return false;
|
|
65279
|
-
visitedFunctionNodes.add(calledFunction);
|
|
65280
|
-
const returnedValues = isNodeOfType(calledFunction.body, "BlockStatement") ? getReturnedValues(calledFunction.body) : [calledFunction.body];
|
|
65281
|
-
const isPotentiallyRendered = returnedValues.length > 0 && returnedValues.every((returnedValue) => isPotentiallyRenderedValueInternal(returnedValue, scopes, visitedFunctionNodes));
|
|
65282
|
-
visitedFunctionNodes.delete(calledFunction);
|
|
65283
|
-
return isPotentiallyRendered;
|
|
65284
|
-
};
|
|
65285
|
-
const isPotentiallyRenderedValue = (node, scopes) => isPotentiallyRenderedValueInternal(node, scopes, /* @__PURE__ */ new Set());
|
|
65286
|
-
const findUseStateBindingSymbol = (node, componentOrHookNode, scopes) => {
|
|
65287
|
-
let currentNode = node.parent;
|
|
65288
|
-
while (currentNode && currentNode !== componentOrHookNode) {
|
|
65289
|
-
if (isNodeOfType(currentNode, "CallExpression") && isReactApiCall(currentNode, "useState", scopes, {
|
|
65290
|
-
allowGlobalReactNamespace: true,
|
|
65291
|
-
resolveNamedAliases: true
|
|
65292
|
-
}) && isNodeOfType(currentNode.parent, "VariableDeclarator") && isNodeOfType(currentNode.parent.id, "ArrayPattern")) {
|
|
65293
|
-
const stateBinding = currentNode.parent.id.elements?.[0];
|
|
65294
|
-
return isNodeOfType(stateBinding, "Identifier") ? scopes.symbolFor(stateBinding) : null;
|
|
65295
|
-
}
|
|
65296
|
-
if (isFunctionLike$1(currentNode)) return null;
|
|
65297
|
-
currentNode = currentNode.parent;
|
|
65298
|
-
}
|
|
65299
|
-
return null;
|
|
65300
|
-
};
|
|
65301
|
-
const doesReferenceControlStructuralRenderedValue = (referenceIdentifier) => {
|
|
65302
|
-
let currentNode = referenceIdentifier;
|
|
65303
|
-
let parentNode = currentNode.parent;
|
|
65304
|
-
while (parentNode) {
|
|
65305
|
-
if (isNodeOfType(parentNode, "ConditionalExpression") && parentNode.test === currentNode && (isStructuralRenderedValue(parentNode.consequent) || isStructuralRenderedValue(parentNode.alternate))) return true;
|
|
65306
|
-
if (isNodeOfType(parentNode, "LogicalExpression") && parentNode.left === currentNode && isStructuralRenderedValue(parentNode.right)) return true;
|
|
65307
|
-
if (isNodeOfType(parentNode, "JSXExpressionContainer") || isFunctionLike$1(parentNode)) return false;
|
|
65308
|
-
currentNode = parentNode;
|
|
65309
|
-
parentNode = currentNode.parent;
|
|
65310
|
-
}
|
|
65311
|
-
return false;
|
|
65312
|
-
};
|
|
65313
|
-
const isRenderedHydrationConsumer = (node, producerHookNode, scopes) => {
|
|
65314
|
-
const renderingComponent = findRenderPhaseComponentOrHook(node, scopes);
|
|
65315
|
-
return Boolean(renderingComponent && renderingComponent !== producerHookNode && isInRenderedOutput(node, renderingComponent, scopes) && !isGatedByFalsyInitialState(node, scopes) && !isAfterClientOnlyEarlyReturn(node, renderingComponent, scopes) && (!hasSuppressHydrationWarningAttribute(findEnclosingJsxOpeningElement(node)) || doesReferenceControlStructuralRenderedValue(node)));
|
|
65316
|
-
};
|
|
65317
|
-
const doesConsumerExpressionReachRenderedOutput = (node, producerHookNode, scopes, visitedSymbolIds) => {
|
|
65318
|
-
if (isRenderedHydrationConsumer(node, producerHookNode, scopes)) return true;
|
|
65319
|
-
const parentNode = node.parent;
|
|
65320
|
-
if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== node || !isNodeOfType(parentNode.id, "Identifier")) return false;
|
|
65321
|
-
const aliasSymbol = scopes.symbolFor(parentNode.id);
|
|
65322
|
-
if (!aliasSymbol || visitedSymbolIds.has(aliasSymbol.id)) return false;
|
|
65323
|
-
visitedSymbolIds.add(aliasSymbol.id);
|
|
65324
|
-
const doesReachRenderedOutput = aliasSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, visitedSymbolIds));
|
|
65325
|
-
visitedSymbolIds.delete(aliasSymbol.id);
|
|
65326
|
-
return doesReachRenderedOutput;
|
|
65327
|
-
};
|
|
65328
|
-
const doesConsumerBindingReachRenderedOutput = (bindingIdentifier, producerHookNode, scopes) => {
|
|
65329
|
-
if (!isNodeOfType(bindingIdentifier, "Identifier")) return false;
|
|
65330
|
-
const consumerSymbol = scopes.symbolFor(bindingIdentifier);
|
|
65331
|
-
if (!consumerSymbol) return false;
|
|
65332
|
-
return consumerSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, new Set([consumerSymbol.id])));
|
|
65333
|
-
};
|
|
65334
|
-
const getReturnedStatePaths = (returnedValue, stateSymbol, scopes) => {
|
|
65335
|
-
const unwrappedValue = stripParenExpression(returnedValue);
|
|
65336
|
-
if (isNodeOfType(unwrappedValue, "ObjectExpression")) return unwrappedValue.properties.flatMap((property) => {
|
|
65337
|
-
if (!isNodeOfType(property, "Property") || property.kind !== "init" || !doesNodeReadSymbol(property.value, stateSymbol)) return [];
|
|
65338
|
-
const propertyName = getResolvedStaticPropertyName(property, scopes);
|
|
65339
|
-
return propertyName === null ? [] : [{
|
|
65340
|
-
kind: "property",
|
|
65341
|
-
key: propertyName
|
|
65342
|
-
}];
|
|
65343
|
-
});
|
|
65344
|
-
if (isNodeOfType(unwrappedValue, "ArrayExpression")) return (unwrappedValue.elements ?? []).flatMap((element, index) => element && isAstNode(element) && doesNodeReadSymbol(element, stateSymbol) ? [{
|
|
65345
|
-
kind: "index",
|
|
65346
|
-
key: String(index)
|
|
65347
|
-
}] : []);
|
|
65348
|
-
return doesNodeReadSymbol(unwrappedValue, stateSymbol) ? [{
|
|
65349
|
-
kind: "direct",
|
|
65350
|
-
key: null
|
|
65351
|
-
}] : [];
|
|
65352
|
-
};
|
|
65353
|
-
const doesCallResultPathReachRenderedOutput = (callExpression, returnedStatePath, producerHookNode, scopes) => {
|
|
65354
|
-
const callParent = callExpression.parent;
|
|
65355
|
-
if (returnedStatePath.kind === "direct") return doesConsumerExpressionReachRenderedOutput(callExpression, producerHookNode, scopes, /* @__PURE__ */ new Set());
|
|
65356
|
-
if (isNodeOfType(callParent, "MemberExpression") && callParent.object === callExpression && getResolvedStaticPropertyName(callParent, scopes, {
|
|
65357
|
-
allowConstNumericLiteral: true,
|
|
65358
|
-
stringifyNonStringLiterals: true
|
|
65359
|
-
}) === returnedStatePath.key) return doesConsumerExpressionReachRenderedOutput(callParent, producerHookNode, scopes, /* @__PURE__ */ new Set());
|
|
65360
|
-
if (!isNodeOfType(callParent, "VariableDeclarator") || callParent.init !== callExpression) return false;
|
|
65361
|
-
if (returnedStatePath.kind === "property" && isNodeOfType(callParent.id, "ObjectPattern")) return callParent.id.properties.some((property) => isNodeOfType(property, "Property") && getResolvedStaticPropertyName(property, scopes) === returnedStatePath.key && doesConsumerBindingReachRenderedOutput(property.value, producerHookNode, scopes));
|
|
65362
|
-
if (returnedStatePath.kind === "index" && isNodeOfType(callParent.id, "ArrayPattern")) {
|
|
65363
|
-
const element = callParent.id.elements?.[Number(returnedStatePath.key)];
|
|
65364
|
-
return Boolean(element && doesConsumerBindingReachRenderedOutput(element, producerHookNode, scopes));
|
|
65365
|
-
}
|
|
65366
|
-
if (!isNodeOfType(callParent.id, "Identifier")) return false;
|
|
65367
|
-
const resultSymbol = scopes.symbolFor(callParent.id);
|
|
65368
|
-
if (!resultSymbol) return false;
|
|
65369
|
-
return resultSymbol.references.some((reference) => {
|
|
65370
|
-
const memberExpression = reference.identifier.parent;
|
|
65371
|
-
return Boolean(isNodeOfType(memberExpression, "MemberExpression") && memberExpression.object === reference.identifier && getResolvedStaticPropertyName(memberExpression, scopes, {
|
|
65372
|
-
allowConstNumericLiteral: true,
|
|
65373
|
-
stringifyNonStringLiterals: true
|
|
65374
|
-
}) === returnedStatePath.key && doesConsumerExpressionReachRenderedOutput(memberExpression, producerHookNode, scopes, new Set([resultSymbol.id])));
|
|
65375
|
-
});
|
|
65376
|
-
};
|
|
65377
|
-
const isReturnedUseStateInitializerRendered = (node, componentOrHookNode, scopes) => {
|
|
65378
|
-
if (!isFunctionLike$1(componentOrHookNode)) return false;
|
|
65379
|
-
const stateSymbol = findUseStateBindingSymbol(node, componentOrHookNode, scopes);
|
|
65380
|
-
if (!stateSymbol) return false;
|
|
65381
|
-
const returnedStatePaths = (isNodeOfType(componentOrHookNode.body, "BlockStatement") ? getReturnedValues(componentOrHookNode.body) : [componentOrHookNode.body]).flatMap((returnedValue) => getReturnedStatePaths(returnedValue, stateSymbol, scopes));
|
|
65382
|
-
if (returnedStatePaths.length === 0) return false;
|
|
65383
|
-
const functionBinding = getDirectFunctionBindingIdentifier(componentOrHookNode);
|
|
65384
|
-
if (!isNodeOfType(functionBinding, "Identifier")) return false;
|
|
65385
|
-
const functionSymbol = scopes.symbolFor(functionBinding);
|
|
65386
|
-
if (!functionSymbol) return false;
|
|
65387
|
-
return functionSymbol.references.some((functionReference) => {
|
|
65388
|
-
const callExpression = functionReference.identifier.parent;
|
|
65389
|
-
return Boolean(isNodeOfType(callExpression, "CallExpression") && callExpression.callee === functionReference.identifier && returnedStatePaths.some((returnedStatePath) => doesCallResultPathReachRenderedOutput(callExpression, returnedStatePath, componentOrHookNode, scopes)));
|
|
65390
|
-
});
|
|
65391
|
-
};
|
|
65392
64494
|
const findFollowingReturnedValues = (ifStatement) => {
|
|
65393
64495
|
const parentNode = ifStatement.parent;
|
|
65394
64496
|
if (!isNodeOfType(parentNode, "BlockStatement")) return [];
|
|
@@ -65401,24 +64503,24 @@ const findFollowingReturnedValues = (ifStatement) => {
|
|
|
65401
64503
|
}
|
|
65402
64504
|
return returnedValues;
|
|
65403
64505
|
};
|
|
65404
|
-
const areConditionExpressionsEquivalent = (leftExpression, rightExpression
|
|
64506
|
+
const areConditionExpressionsEquivalent = (leftExpression, rightExpression) => {
|
|
65405
64507
|
const left = stripParenExpression(leftExpression);
|
|
65406
64508
|
const right = stripParenExpression(rightExpression);
|
|
65407
|
-
if (areExpressionsStructurallyEqual(left, right)) return
|
|
64509
|
+
if (areExpressionsStructurallyEqual(left, right)) return true;
|
|
65408
64510
|
if (left.type !== right.type) return false;
|
|
65409
|
-
if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument
|
|
65410
|
-
if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left
|
|
65411
|
-
if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left
|
|
64511
|
+
if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument);
|
|
64512
|
+
if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
|
|
64513
|
+
if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
|
|
65412
64514
|
return false;
|
|
65413
64515
|
};
|
|
65414
|
-
const areReturnTreesEquivalent = (leftStatement, rightStatement
|
|
64516
|
+
const areReturnTreesEquivalent = (leftStatement, rightStatement) => {
|
|
65415
64517
|
if (!leftStatement || !rightStatement) return leftStatement === rightStatement;
|
|
65416
|
-
if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument
|
|
65417
|
-
if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test
|
|
64518
|
+
if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument);
|
|
64519
|
+
if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate);
|
|
65418
64520
|
if (!isNodeOfType(leftStatement, "BlockStatement") || !isNodeOfType(rightStatement, "BlockStatement")) return false;
|
|
65419
64521
|
const leftReturningStatements = leftStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
|
|
65420
64522
|
const rightReturningStatements = rightStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
|
|
65421
|
-
return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]
|
|
64523
|
+
return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]));
|
|
65422
64524
|
};
|
|
65423
64525
|
const isStructuralRenderedValue = (node) => {
|
|
65424
64526
|
if (!node) return false;
|
|
@@ -65442,22 +64544,19 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
|
|
|
65442
64544
|
if (isTestlikeFilename(context.filename)) return {};
|
|
65443
64545
|
if (classifyReactNativeFileTarget(context) === "react-native") return {};
|
|
65444
64546
|
let fileHasUseClientDirective = false;
|
|
65445
|
-
let fileHasExplicitReactRuntimeReference = false;
|
|
65446
64547
|
let fileIsEmailTemplate = false;
|
|
65447
64548
|
const reportedNodes = /* @__PURE__ */ new Set();
|
|
65448
|
-
const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext
|
|
64549
|
+
const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext) => {
|
|
65449
64550
|
const conditionMatch = matchHydrationCondition(conditionNode, context);
|
|
65450
64551
|
if (!conditionMatch) return;
|
|
65451
64552
|
const { predicateMatch, predicateNode } = conditionMatch;
|
|
65452
64553
|
if (reportedNodes.has(predicateNode)) return;
|
|
65453
|
-
if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch
|
|
65454
|
-
const
|
|
65455
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
|
|
64554
|
+
if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch)) return;
|
|
64555
|
+
const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes);
|
|
65456
64556
|
if (!componentOrHookNode) return;
|
|
65457
|
-
|
|
65458
|
-
if (!
|
|
65459
|
-
if (
|
|
65460
|
-
if (!hasProvenRenderedConsumer && !(requiresRenderedContext ? isPotentiallyRenderedValue(leftBranch, context.scopes) : isRenderedValue(leftBranch, context.scopes)) && (!rightBranch || !(requiresRenderedContext ? isPotentiallyRenderedValue(rightBranch, context.scopes) : isRenderedValue(rightBranch, context.scopes)))) {
|
|
64557
|
+
if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
|
|
64558
|
+
if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes)) return;
|
|
64559
|
+
if (!isRenderedValue(leftBranch) && (!rightBranch || !isRenderedValue(rightBranch))) {
|
|
65461
64560
|
const attribute = findEnclosingJsxAttribute(conditionNode);
|
|
65462
64561
|
if (!attribute || isEventHandlerAttribute(attribute)) return;
|
|
65463
64562
|
}
|
|
@@ -65476,31 +64575,28 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
|
|
|
65476
64575
|
return {
|
|
65477
64576
|
Program(node) {
|
|
65478
64577
|
fileHasUseClientDirective = hasDirective(node, "use client");
|
|
65479
|
-
fileHasExplicitReactRuntimeReference = containsExplicitReactRuntimeReference(node, context.scopes);
|
|
65480
64578
|
fileIsEmailTemplate = hasEmailTemplateImport(node);
|
|
65481
64579
|
},
|
|
65482
64580
|
ConditionalExpression(node) {
|
|
65483
64581
|
reportHydrationBranch(node.test, node.consequent, node.alternate, true);
|
|
65484
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(node, context.scopes);
|
|
65485
|
-
if (componentOrHookNode && isReturnedUseStateInitializerRendered(node, componentOrHookNode, context.scopes)) reportHydrationBranch(node.test, node.consequent, node.alternate, false, true);
|
|
65486
64582
|
},
|
|
65487
64583
|
LogicalExpression(node) {
|
|
65488
64584
|
if (node.operator !== "&&" && node.operator !== "||") return;
|
|
65489
|
-
const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right
|
|
64585
|
+
const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right) : isRenderedValue(node.right) ? node.right : null;
|
|
65490
64586
|
if (!renderedValue) return;
|
|
65491
64587
|
reportHydrationBranch(node, renderedValue, null, true);
|
|
65492
64588
|
},
|
|
65493
64589
|
IfStatement(node) {
|
|
65494
|
-
if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate
|
|
64590
|
+
if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate)) return;
|
|
65495
64591
|
const consequentValues = getReturnedValues(node.consequent);
|
|
65496
64592
|
const alternateValues = node.alternate ? getReturnedValues(node.alternate) : findFollowingReturnedValues(node);
|
|
65497
64593
|
if (consequentValues.length === 0 || alternateValues.length === 0) return;
|
|
65498
|
-
const
|
|
65499
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
|
|
64594
|
+
const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
|
|
65500
64595
|
if (!componentOrHookNode) return;
|
|
65501
|
-
|
|
64596
|
+
const enclosingFunction = findEnclosingFunction$1(node);
|
|
64597
|
+
if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
|
|
65502
64598
|
for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
|
|
65503
|
-
if (!isRenderedValue(consequentValue
|
|
64599
|
+
if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
|
|
65504
64600
|
reportHydrationBranch(node.test, consequentValue, alternateValue, false);
|
|
65505
64601
|
}
|
|
65506
64602
|
}
|
|
@@ -67234,124 +66330,6 @@ const isInsideSnapshotHelper = (node) => {
|
|
|
67234
66330
|
}
|
|
67235
66331
|
return false;
|
|
67236
66332
|
};
|
|
67237
|
-
const findEnclosingNextjsPageDataFunction = (node) => {
|
|
67238
|
-
let outermostFunction = null;
|
|
67239
|
-
let cursor = node.parent;
|
|
67240
|
-
while (cursor) {
|
|
67241
|
-
if (isFunctionLike$1(cursor)) outermostFunction = cursor;
|
|
67242
|
-
if (isNodeOfType(cursor, "Program")) {
|
|
67243
|
-
if (!outermostFunction) return null;
|
|
67244
|
-
for (const exportName of NEXTJS_PAGE_DATA_EXPORT_NAMES) {
|
|
67245
|
-
const exportedValue = findExportedValue(cursor, exportName);
|
|
67246
|
-
if (exportedValue && isAstDescendant(outermostFunction, exportedValue)) return outermostFunction;
|
|
67247
|
-
}
|
|
67248
|
-
return null;
|
|
67249
|
-
}
|
|
67250
|
-
cursor = cursor.parent ?? null;
|
|
67251
|
-
}
|
|
67252
|
-
return null;
|
|
67253
|
-
};
|
|
67254
|
-
const findConditionalReturnExpressionRoot = (node) => {
|
|
67255
|
-
let expressionRoot = findTransparentExpressionRoot(node);
|
|
67256
|
-
while (expressionRoot.parent && isNodeOfType(expressionRoot.parent, "ConditionalExpression") && (expressionRoot.parent.consequent === expressionRoot || expressionRoot.parent.alternate === expressionRoot)) expressionRoot = findTransparentExpressionRoot(expressionRoot.parent);
|
|
67257
|
-
return expressionRoot;
|
|
67258
|
-
};
|
|
67259
|
-
const isReturnedPageDataResultBinding = (returnExpression, pageDataFunction, context) => {
|
|
67260
|
-
const declarator = returnExpression.parent;
|
|
67261
|
-
if (!isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== returnExpression || !isNodeOfType(declarator.id, "Identifier") || findEnclosingFunction$1(declarator) !== pageDataFunction) return false;
|
|
67262
|
-
const bindingSymbol = context.scopes.symbolFor(declarator.id);
|
|
67263
|
-
if (!bindingSymbol || bindingSymbol.references.length !== 1) return false;
|
|
67264
|
-
const referenceRoot = findTransparentExpressionRoot(bindingSymbol.references[0].identifier);
|
|
67265
|
-
const returnStatement = referenceRoot.parent;
|
|
67266
|
-
return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === referenceRoot && findEnclosingFunction$1(returnStatement) === pageDataFunction;
|
|
67267
|
-
};
|
|
67268
|
-
const isSameShorthandPropertyValue = (node, property) => property.shorthand && (node === property.key || node === property.value);
|
|
67269
|
-
const isValueForwardedThroughLiteralStructure = (node, structure) => {
|
|
67270
|
-
const strippedNode = stripParenExpression(node);
|
|
67271
|
-
const strippedStructure = stripParenExpression(structure);
|
|
67272
|
-
if (strippedNode === strippedStructure) return true;
|
|
67273
|
-
if (isNodeOfType(strippedStructure, "ConditionalExpression")) return isValueForwardedThroughLiteralStructure(strippedNode, strippedStructure.consequent) || isValueForwardedThroughLiteralStructure(strippedNode, strippedStructure.alternate);
|
|
67274
|
-
if (isNodeOfType(strippedStructure, "ArrayExpression")) return strippedStructure.elements.some((element) => element && !isNodeOfType(element, "SpreadElement") && isValueForwardedThroughLiteralStructure(strippedNode, element));
|
|
67275
|
-
if (!isNodeOfType(strippedStructure, "ObjectExpression")) return false;
|
|
67276
|
-
return strippedStructure.properties.some((property) => {
|
|
67277
|
-
if (isNodeOfType(property, "SpreadElement")) return isValueForwardedThroughLiteralStructure(strippedNode, property.argument);
|
|
67278
|
-
if (!isNodeOfType(property, "Property")) return false;
|
|
67279
|
-
if (isValueForwardedThroughLiteralStructure(strippedNode, property.value)) return true;
|
|
67280
|
-
return isSameShorthandPropertyValue(strippedNode, property);
|
|
67281
|
-
});
|
|
67282
|
-
};
|
|
67283
|
-
const isValueForwardedToPropertyValue = (node, property) => {
|
|
67284
|
-
const directValue = findConditionalReturnExpressionRoot(node);
|
|
67285
|
-
if (isValueForwardedThroughLiteralStructure(directValue, property.value)) return true;
|
|
67286
|
-
return isSameShorthandPropertyValue(directValue, property);
|
|
67287
|
-
};
|
|
67288
|
-
const isInsideReturnedNextjsProps = (node, pageDataFunction, context) => {
|
|
67289
|
-
let cursor = node.parent;
|
|
67290
|
-
while (cursor && cursor !== pageDataFunction) {
|
|
67291
|
-
if (isNodeOfType(cursor, "Property") && getStaticPropertyKeyName(cursor, { allowComputedString: true }) === "props" && isValueForwardedToPropertyValue(node, cursor)) {
|
|
67292
|
-
const propertyContainer = cursor.parent;
|
|
67293
|
-
if (!propertyContainer) return false;
|
|
67294
|
-
const returnExpression = findConditionalReturnExpressionRoot(propertyContainer);
|
|
67295
|
-
const returnStatement = returnExpression.parent;
|
|
67296
|
-
if (isNodeOfType(returnStatement, "ReturnStatement") && findEnclosingFunction$1(returnStatement) === pageDataFunction) return true;
|
|
67297
|
-
if (isNodeOfType(pageDataFunction, "ArrowFunctionExpression") && !isNodeOfType(pageDataFunction.body, "BlockStatement") && stripParenExpression(pageDataFunction.body) === stripParenExpression(returnExpression)) return true;
|
|
67298
|
-
if (isReturnedPageDataResultBinding(returnExpression, pageDataFunction, context)) return true;
|
|
67299
|
-
}
|
|
67300
|
-
cursor = cursor.parent ?? null;
|
|
67301
|
-
}
|
|
67302
|
-
return false;
|
|
67303
|
-
};
|
|
67304
|
-
const isExpressionReturnedByFunction = (node, functionNode) => {
|
|
67305
|
-
const returnExpression = findConditionalReturnExpressionRoot(node);
|
|
67306
|
-
if (isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode.body, "BlockStatement")) return stripParenExpression(functionNode.body) === stripParenExpression(returnExpression);
|
|
67307
|
-
const returnStatement = returnExpression.parent;
|
|
67308
|
-
return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === returnExpression && findEnclosingFunction$1(returnStatement) === functionNode;
|
|
67309
|
-
};
|
|
67310
|
-
const isValueForwardedToBindingInitializer = (node, bindingInitializer) => {
|
|
67311
|
-
if (isValueForwardedThroughLiteralStructure(findConditionalReturnExpressionRoot(node), bindingInitializer)) return true;
|
|
67312
|
-
const initializer = stripParenExpression(bindingInitializer);
|
|
67313
|
-
if (!isNodeOfType(initializer, "CallExpression")) return false;
|
|
67314
|
-
const callee = stripParenExpression(initializer.callee);
|
|
67315
|
-
return isFunctionLike$1(callee) && isExpressionReturnedByFunction(node, callee);
|
|
67316
|
-
};
|
|
67317
|
-
const findPageDataResultBinding = (node) => {
|
|
67318
|
-
let cursor = node.parent;
|
|
67319
|
-
while (cursor) {
|
|
67320
|
-
if (isNodeOfType(cursor, "VariableDeclarator")) {
|
|
67321
|
-
if (cursor.init && isNodeOfType(cursor.id, "Identifier") && isValueForwardedToBindingInitializer(node, cursor.init)) return cursor.id;
|
|
67322
|
-
return null;
|
|
67323
|
-
}
|
|
67324
|
-
cursor = cursor.parent ?? null;
|
|
67325
|
-
}
|
|
67326
|
-
return null;
|
|
67327
|
-
};
|
|
67328
|
-
const isUsedToSerializeNextjsPageProps = (node, context) => {
|
|
67329
|
-
if (!isInProjectDirectory(context, "pages") || isInProjectDirectory(context, "pages/api")) return false;
|
|
67330
|
-
const pageDataFunction = findEnclosingNextjsPageDataFunction(node);
|
|
67331
|
-
if (!pageDataFunction) return false;
|
|
67332
|
-
if (isInsideReturnedNextjsProps(node, pageDataFunction, context)) return true;
|
|
67333
|
-
const bindingIdentifier = findPageDataResultBinding(node);
|
|
67334
|
-
const bindingSymbol = bindingIdentifier ? context.scopes.symbolFor(bindingIdentifier) : null;
|
|
67335
|
-
if (!bindingSymbol) return false;
|
|
67336
|
-
const aliasSymbols = collectConstAliasSymbols(bindingSymbol, context.scopes);
|
|
67337
|
-
const aliasSymbolIds = new Set(aliasSymbols.map((aliasSymbol) => aliasSymbol.id));
|
|
67338
|
-
let hasPagePropsReference = false;
|
|
67339
|
-
for (const aliasSymbol of aliasSymbols) for (const reference of aliasSymbol.references) {
|
|
67340
|
-
if (findEnclosingFunction$1(reference.identifier) !== pageDataFunction) return false;
|
|
67341
|
-
if (isInsideReturnedNextjsProps(reference.identifier, pageDataFunction, context)) {
|
|
67342
|
-
hasPagePropsReference = true;
|
|
67343
|
-
continue;
|
|
67344
|
-
}
|
|
67345
|
-
const referenceRoot = findTransparentExpressionRoot(reference.identifier);
|
|
67346
|
-
const declarator = referenceRoot.parent;
|
|
67347
|
-
if (isNodeOfType(declarator, "VariableDeclarator") && declarator.init === referenceRoot && isNodeOfType(declarator.id, "Identifier")) {
|
|
67348
|
-
const aliasSymbolForReference = context.scopes.symbolFor(declarator.id);
|
|
67349
|
-
if (aliasSymbolForReference && aliasSymbolIds.has(aliasSymbolForReference.id)) continue;
|
|
67350
|
-
}
|
|
67351
|
-
return false;
|
|
67352
|
-
}
|
|
67353
|
-
return hasPagePropsReference;
|
|
67354
|
-
};
|
|
67355
66333
|
const noJsonParseStringifyClone = defineRule({
|
|
67356
66334
|
id: "no-json-parse-stringify-clone",
|
|
67357
66335
|
title: "JSON parse/stringify deep clone",
|
|
@@ -67369,7 +66347,6 @@ const noJsonParseStringifyClone = defineRule({
|
|
|
67369
66347
|
if (isInsideSnapshotHelper(node)) return;
|
|
67370
66348
|
if (isAssignedToNormalizationBinding(node)) return;
|
|
67371
66349
|
if (isCatchParameterRoundTrip(firstArgument)) return;
|
|
67372
|
-
if (isUsedToSerializeNextjsPageProps(node, context)) return;
|
|
67373
66350
|
context.report({
|
|
67374
66351
|
node,
|
|
67375
66352
|
message: MESSAGE$35
|
|
@@ -68785,77 +67762,6 @@ const isCancellationGuardTest = (test) => {
|
|
|
68785
67762
|
});
|
|
68786
67763
|
return matches;
|
|
68787
67764
|
};
|
|
68788
|
-
const getReactRefCurrent = (expression, context) => {
|
|
68789
|
-
const stripped = stripParenExpression(expression);
|
|
68790
|
-
if (!isNodeOfType(stripped, "MemberExpression") || getStaticPropertyName(stripped) !== "current") return null;
|
|
68791
|
-
const receiver = stripParenExpression(stripped.object);
|
|
68792
|
-
if (!isNodeOfType(receiver, "Identifier")) return null;
|
|
68793
|
-
const binding = findVariableInitializer(receiver, receiver.name);
|
|
68794
|
-
const initializer = binding?.initializer ? stripParenExpression(binding.initializer) : null;
|
|
68795
|
-
return initializer && isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, USE_REF_HOOK_NAMES$1, context.scopes, {
|
|
68796
|
-
allowGlobalReactNamespace: true,
|
|
68797
|
-
allowUnboundBareCalls: true
|
|
68798
|
-
}) ? stripped : null;
|
|
68799
|
-
};
|
|
68800
|
-
const getStableOwnershipToken = (expression, context) => {
|
|
68801
|
-
const stripped = stripParenExpression(expression);
|
|
68802
|
-
if (!isNodeOfType(stripped, "Identifier")) return null;
|
|
68803
|
-
const symbol = context.scopes.symbolFor(stripped);
|
|
68804
|
-
const initializer = symbol?.initializer ? stripParenExpression(symbol.initializer) : null;
|
|
68805
|
-
const isStableAsyncIdentity = Boolean(initializer && isNodeOfType(initializer, "ObjectExpression")) || Boolean(initializer && getReactRefCurrent(initializer, context)) || Boolean(initializer && isNodeOfType(initializer, "UpdateExpression") && initializer.operator === "++" && getReactRefCurrent(initializer.argument, context));
|
|
68806
|
-
return symbol && symbol.kind === "const" && symbol.references.every((reference) => reference.flag === "read") && isStableAsyncIdentity ? stripped : null;
|
|
68807
|
-
};
|
|
68808
|
-
const getAsyncOwnershipComparison = (test, context) => {
|
|
68809
|
-
const stripped = stripParenExpression(test);
|
|
68810
|
-
if (!isNodeOfType(stripped, "BinaryExpression")) return null;
|
|
68811
|
-
const leftRef = getReactRefCurrent(stripped.left, context);
|
|
68812
|
-
const rightRef = getReactRefCurrent(stripped.right, context);
|
|
68813
|
-
const leftToken = getStableOwnershipToken(stripped.left, context);
|
|
68814
|
-
const rightToken = getStableOwnershipToken(stripped.right, context);
|
|
68815
|
-
if (stripped.operator === "===" || stripped.operator === "==") {
|
|
68816
|
-
if (leftRef && rightToken) return {
|
|
68817
|
-
refCurrent: leftRef,
|
|
68818
|
-
token: rightToken,
|
|
68819
|
-
mode: "owns",
|
|
68820
|
-
isOrdered: false
|
|
68821
|
-
};
|
|
68822
|
-
if (rightRef && leftToken) return {
|
|
68823
|
-
refCurrent: rightRef,
|
|
68824
|
-
token: leftToken,
|
|
68825
|
-
mode: "owns",
|
|
68826
|
-
isOrdered: false
|
|
68827
|
-
};
|
|
68828
|
-
return null;
|
|
68829
|
-
}
|
|
68830
|
-
if (stripped.operator === "!==" || stripped.operator === "!=") {
|
|
68831
|
-
if (leftRef && rightToken) return {
|
|
68832
|
-
refCurrent: leftRef,
|
|
68833
|
-
token: rightToken,
|
|
68834
|
-
mode: "lost",
|
|
68835
|
-
isOrdered: false
|
|
68836
|
-
};
|
|
68837
|
-
if (rightRef && leftToken) return {
|
|
68838
|
-
refCurrent: rightRef,
|
|
68839
|
-
token: leftToken,
|
|
68840
|
-
mode: "lost",
|
|
68841
|
-
isOrdered: false
|
|
68842
|
-
};
|
|
68843
|
-
return null;
|
|
68844
|
-
}
|
|
68845
|
-
if (stripped.operator === "<=" && leftRef && rightToken) return {
|
|
68846
|
-
refCurrent: leftRef,
|
|
68847
|
-
token: rightToken,
|
|
68848
|
-
mode: "owns",
|
|
68849
|
-
isOrdered: true
|
|
68850
|
-
};
|
|
68851
|
-
if (stripped.operator === ">=" && rightRef && leftToken) return {
|
|
68852
|
-
refCurrent: rightRef,
|
|
68853
|
-
token: leftToken,
|
|
68854
|
-
mode: "owns",
|
|
68855
|
-
isOrdered: true
|
|
68856
|
-
};
|
|
68857
|
-
return null;
|
|
68858
|
-
};
|
|
68859
67765
|
const dedupeCatchPathStates = (states) => {
|
|
68860
67766
|
const statesByKey = /* @__PURE__ */ new Map();
|
|
68861
67767
|
for (const state of states) statesByKey.set(`${Number(state.isCleared)}:${Number(state.isCancellationPath)}`, state);
|
|
@@ -69188,221 +68094,7 @@ const isInsideTryFinalizer = (node, tryStatement) => {
|
|
|
69188
68094
|
}
|
|
69189
68095
|
return false;
|
|
69190
68096
|
};
|
|
69191
|
-
const
|
|
69192
|
-
let entry = node;
|
|
69193
|
-
let cursor = node.parent;
|
|
69194
|
-
while (cursor && cursor !== functionNode) {
|
|
69195
|
-
if (isNodeOfType(cursor, "BlockStatement")) return {
|
|
69196
|
-
block: cursor,
|
|
69197
|
-
entry
|
|
69198
|
-
};
|
|
69199
|
-
entry = cursor;
|
|
69200
|
-
cursor = cursor.parent ?? null;
|
|
69201
|
-
}
|
|
69202
|
-
return null;
|
|
69203
|
-
};
|
|
69204
|
-
const claimPrecedesTruthySet = (claimNode, truthySet, firstRiskyAwait, functionNode, context) => {
|
|
69205
|
-
const claimStart = getNodeStart$1(claimNode);
|
|
69206
|
-
if (claimStart === null || claimStart >= firstRiskyAwait.start || truthySet.start >= firstRiskyAwait.start) return false;
|
|
69207
|
-
const claimEntry = getDirectBlockEntry(claimNode, functionNode);
|
|
69208
|
-
const truthyEntry = getDirectBlockEntry(truthySet.node, functionNode);
|
|
69209
|
-
if (!claimEntry || !truthyEntry || claimEntry.block !== truthyEntry.block) return false;
|
|
69210
|
-
let claimCursor = claimNode.parent;
|
|
69211
|
-
while (claimCursor && claimCursor !== claimEntry.block) {
|
|
69212
|
-
if (isNodeOfType(claimCursor, "IfStatement") || isNodeOfType(claimCursor, "SwitchCase") || isNodeOfType(claimCursor, "ConditionalExpression") || isNodeOfType(claimCursor, "LogicalExpression") || isNodeOfType(claimCursor, "ForStatement") || isNodeOfType(claimCursor, "ForInStatement") || isNodeOfType(claimCursor, "ForOfStatement") || isNodeOfType(claimCursor, "WhileStatement") || isNodeOfType(claimCursor, "DoWhileStatement")) return false;
|
|
69213
|
-
claimCursor = claimCursor.parent ?? null;
|
|
69214
|
-
}
|
|
69215
|
-
const claimIndex = claimEntry.block.body.findIndex((statement) => statement === claimEntry.entry);
|
|
69216
|
-
const truthyIndex = claimEntry.block.body.findIndex((statement) => statement === truthyEntry.entry);
|
|
69217
|
-
if (claimIndex === -1 || truthyIndex === -1 || claimIndex >= truthyIndex) return false;
|
|
69218
|
-
return claimEntry.block.body.slice(claimIndex + 1, truthyIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context));
|
|
69219
|
-
};
|
|
69220
|
-
const getOwningFunction = (functionNode) => {
|
|
69221
|
-
let ownerFunction = functionNode;
|
|
69222
|
-
let cursor = functionNode.parent;
|
|
69223
|
-
while (cursor) {
|
|
69224
|
-
if (isFunctionLike$1(cursor)) ownerFunction = cursor;
|
|
69225
|
-
cursor = cursor.parent ?? null;
|
|
69226
|
-
}
|
|
69227
|
-
return ownerFunction;
|
|
69228
|
-
};
|
|
69229
|
-
const isEffectInvalidationPairedWithReset = (writeNode, truthySets, context) => {
|
|
69230
|
-
const truthyCall = truthySets[0]?.node;
|
|
69231
|
-
if (!truthyCall || !isNodeOfType(truthyCall, "CallExpression")) return false;
|
|
69232
|
-
const setter = getSetterBooleanValue(truthyCall, context);
|
|
69233
|
-
if (!setter) return false;
|
|
69234
|
-
let effectCallback = writeNode.parent;
|
|
69235
|
-
while (effectCallback && !isFunctionLike$1(effectCallback)) effectCallback = effectCallback.parent ?? null;
|
|
69236
|
-
if (!effectCallback || !isEffectCallback(effectCallback, context)) return false;
|
|
69237
|
-
if (!isUnconditionallyExecutedWithinFunction(writeNode, effectCallback, context)) return false;
|
|
69238
|
-
const writeEntry = getDirectBlockEntry(writeNode, effectCallback);
|
|
69239
|
-
if (!writeEntry) return false;
|
|
69240
|
-
let isPaired = false;
|
|
69241
|
-
walkOwnFunctionScope(effectCallback, (candidate) => {
|
|
69242
|
-
if (isPaired || !isNodeOfType(candidate, "CallExpression")) return;
|
|
69243
|
-
const candidateSetter = getSetterBooleanValue(candidate, context);
|
|
69244
|
-
if (candidateSetter?.setterKey !== setter.setterKey || candidateSetter.value || !isUnconditionallyExecutedWithinFunction(candidate, effectCallback, context)) return;
|
|
69245
|
-
const resetEntry = getDirectBlockEntry(candidate, effectCallback);
|
|
69246
|
-
if (!resetEntry || resetEntry.block !== writeEntry.block) return;
|
|
69247
|
-
const writeIndex = writeEntry.block.body.findIndex((statement) => statement === writeEntry.entry);
|
|
69248
|
-
const resetIndex = resetEntry.block.body.findIndex((statement) => statement === resetEntry.entry);
|
|
69249
|
-
if (writeIndex === -1 || resetIndex === -1) return;
|
|
69250
|
-
if (resetIndex <= writeIndex) {
|
|
69251
|
-
isPaired = true;
|
|
69252
|
-
return false;
|
|
69253
|
-
}
|
|
69254
|
-
isPaired = writeEntry.block.body.slice(writeIndex + 1, resetIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, effectCallback, context));
|
|
69255
|
-
return isPaired ? false : void 0;
|
|
69256
|
-
});
|
|
69257
|
-
return isPaired;
|
|
69258
|
-
};
|
|
69259
|
-
const isUnconditionalReturnBranch = (statement) => {
|
|
69260
|
-
if (isNodeOfType(statement, "ReturnStatement")) return true;
|
|
69261
|
-
return Boolean(isNodeOfType(statement, "BlockStatement") && statement.body.length === 1 && isNodeOfType(statement.body[0], "ReturnStatement"));
|
|
69262
|
-
};
|
|
69263
|
-
const findSingleFlightSnapshotClaim = (tokenInitializer, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
|
|
69264
|
-
const snapshotEntry = getDirectBlockEntry(tokenInitializer, functionNode);
|
|
69265
|
-
const resetEntry = getDirectBlockEntry(resetNode, functionNode);
|
|
69266
|
-
if (!snapshotEntry || !resetEntry) return null;
|
|
69267
|
-
const claimCandidates = [];
|
|
69268
|
-
const releaseCandidates = [];
|
|
69269
|
-
walkOwnFunctionScope(functionNode, (candidate) => {
|
|
69270
|
-
if (!isNodeOfType(candidate, "AssignmentExpression") || candidate.operator !== "=" || !getReactRefCurrent(candidate.left, context)) return;
|
|
69271
|
-
const assignedValue = stripParenExpression(candidate.right);
|
|
69272
|
-
if (!isNodeOfType(assignedValue, "Literal") || typeof assignedValue.value !== "boolean") return;
|
|
69273
|
-
const candidateKey = serializeReferenceKey({
|
|
69274
|
-
node: candidate.left,
|
|
69275
|
-
scopes: context.scopes
|
|
69276
|
-
});
|
|
69277
|
-
if (!candidateKey) return;
|
|
69278
|
-
if (!assignedValue.value) {
|
|
69279
|
-
if (getDirectBlockEntry(candidate, functionNode)?.block === resetEntry.block) releaseCandidates.push(candidate);
|
|
69280
|
-
return;
|
|
69281
|
-
}
|
|
69282
|
-
if (!truthySets.some((truthySet) => claimPrecedesTruthySet(candidate, truthySet, firstRiskyAwait, functionNode, context))) return;
|
|
69283
|
-
const candidateEntry = getDirectBlockEntry(candidate, functionNode);
|
|
69284
|
-
if (!candidateEntry || candidateEntry.block !== snapshotEntry.block) return;
|
|
69285
|
-
const candidateIndex = candidateEntry.block.body.findIndex((statement) => statement === candidateEntry.entry);
|
|
69286
|
-
const snapshotIndex = candidateEntry.block.body.findIndex((statement) => statement === snapshotEntry.entry);
|
|
69287
|
-
if (candidateIndex === -1 || snapshotIndex === -1 || candidateIndex >= snapshotIndex) return;
|
|
69288
|
-
const guardIndex = candidateEntry.block.body.findLastIndex((statement, statementIndex) => {
|
|
69289
|
-
if (statementIndex >= candidateIndex || !isNodeOfType(statement, "IfStatement") || statement.alternate !== null || !isUnconditionalReturnBranch(statement.consequent)) return false;
|
|
69290
|
-
return serializeReferenceKey({
|
|
69291
|
-
node: stripParenExpression(statement.test),
|
|
69292
|
-
scopes: context.scopes
|
|
69293
|
-
}) === candidateKey;
|
|
69294
|
-
});
|
|
69295
|
-
if (guardIndex === -1 || !candidateEntry.block.body.slice(guardIndex + 1, candidateIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context))) return;
|
|
69296
|
-
claimCandidates.push(candidate);
|
|
69297
|
-
});
|
|
69298
|
-
const claim = claimCandidates.find((claimCandidate) => {
|
|
69299
|
-
const candidateKey = serializeReferenceKey({
|
|
69300
|
-
node: claimCandidate.left,
|
|
69301
|
-
scopes: context.scopes
|
|
69302
|
-
});
|
|
69303
|
-
return releaseCandidates.some((releaseCandidate) => serializeReferenceKey({
|
|
69304
|
-
node: releaseCandidate.left,
|
|
69305
|
-
scopes: context.scopes
|
|
69306
|
-
}) === candidateKey);
|
|
69307
|
-
});
|
|
69308
|
-
if (!claim) return null;
|
|
69309
|
-
const claimKey = serializeReferenceKey({
|
|
69310
|
-
node: claim.left,
|
|
69311
|
-
scopes: context.scopes
|
|
69312
|
-
});
|
|
69313
|
-
const release = releaseCandidates.find((releaseCandidate) => serializeReferenceKey({
|
|
69314
|
-
node: releaseCandidate.left,
|
|
69315
|
-
scopes: context.scopes
|
|
69316
|
-
}) === claimKey);
|
|
69317
|
-
if (!claimKey || !release) return null;
|
|
69318
|
-
const releaseEntry = getDirectBlockEntry(release, functionNode);
|
|
69319
|
-
if (!releaseEntry || releaseEntry.block !== resetEntry.block) return null;
|
|
69320
|
-
const releaseIndex = resetEntry.block.body.findIndex((statement) => statement === releaseEntry.entry);
|
|
69321
|
-
const resetIndex = resetEntry.block.body.findIndex((statement) => statement === resetEntry.entry);
|
|
69322
|
-
if (releaseIndex === -1 || resetIndex === -1 || !resetEntry.block.body.slice(Math.min(releaseIndex, resetIndex) + 1, Math.max(releaseIndex, resetIndex)).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context))) return null;
|
|
69323
|
-
let didFindUnsafeWrite = false;
|
|
69324
|
-
walkAst(getOwningFunction(functionNode), (candidate) => {
|
|
69325
|
-
if (didFindUnsafeWrite || candidate === claim || candidate === release) return;
|
|
69326
|
-
const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
|
|
69327
|
-
if (writeTarget && serializeReferenceKey({
|
|
69328
|
-
node: writeTarget,
|
|
69329
|
-
scopes: context.scopes
|
|
69330
|
-
}) === claimKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindUnsafeWrite = true;
|
|
69331
|
-
});
|
|
69332
|
-
return didFindUnsafeWrite ? null : claim;
|
|
69333
|
-
};
|
|
69334
|
-
const findOwnershipClaim = (comparison, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
|
|
69335
|
-
const refKey = serializeReferenceKey({
|
|
69336
|
-
node: comparison.refCurrent,
|
|
69337
|
-
scopes: context.scopes
|
|
69338
|
-
});
|
|
69339
|
-
const tokenKey = serializeReferenceKey({
|
|
69340
|
-
node: comparison.token,
|
|
69341
|
-
scopes: context.scopes
|
|
69342
|
-
});
|
|
69343
|
-
if (!refKey || !tokenKey) return null;
|
|
69344
|
-
const candidates = [];
|
|
69345
|
-
const tokenSymbol = context.scopes.symbolFor(comparison.token);
|
|
69346
|
-
const tokenInitializer = tokenSymbol?.initializer ? stripParenExpression(tokenSymbol.initializer) : null;
|
|
69347
|
-
if (comparison.isOrdered && !isNodeOfType(tokenInitializer, "UpdateExpression")) return null;
|
|
69348
|
-
if (tokenInitializer && isNodeOfType(tokenInitializer, "UpdateExpression") && tokenInitializer.operator === "++" && serializeReferenceKey({
|
|
69349
|
-
node: tokenInitializer.argument,
|
|
69350
|
-
scopes: context.scopes
|
|
69351
|
-
}) === refKey) candidates.push(tokenInitializer);
|
|
69352
|
-
if (tokenInitializer && getReactRefCurrent(tokenInitializer, context) && serializeReferenceKey({
|
|
69353
|
-
node: tokenInitializer,
|
|
69354
|
-
scopes: context.scopes
|
|
69355
|
-
}) === refKey) {
|
|
69356
|
-
const singleFlightClaim = findSingleFlightSnapshotClaim(tokenInitializer, functionNode, truthySets, firstRiskyAwait, resetNode, context);
|
|
69357
|
-
if (singleFlightClaim) candidates.push(singleFlightClaim);
|
|
69358
|
-
}
|
|
69359
|
-
if (tokenInitializer && isNodeOfType(tokenInitializer, "UpdateExpression")) {
|
|
69360
|
-
const generationKey = serializeReferenceKey({
|
|
69361
|
-
node: tokenInitializer.argument,
|
|
69362
|
-
scopes: context.scopes
|
|
69363
|
-
});
|
|
69364
|
-
if (generationKey && generationKey === refKey) {
|
|
69365
|
-
const ownerFunction = getOwningFunction(functionNode);
|
|
69366
|
-
let didFindOtherGenerationWrite = false;
|
|
69367
|
-
walkAst(ownerFunction, (candidate) => {
|
|
69368
|
-
if (didFindOtherGenerationWrite || candidate === tokenInitializer) return;
|
|
69369
|
-
const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
|
|
69370
|
-
if (writeTarget && serializeReferenceKey({
|
|
69371
|
-
node: writeTarget,
|
|
69372
|
-
scopes: context.scopes
|
|
69373
|
-
}) === generationKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindOtherGenerationWrite = true;
|
|
69374
|
-
});
|
|
69375
|
-
if (didFindOtherGenerationWrite) return null;
|
|
69376
|
-
}
|
|
69377
|
-
}
|
|
69378
|
-
walkOwnFunctionScope(functionNode, (candidate) => {
|
|
69379
|
-
if (!isNodeOfType(candidate, "AssignmentExpression") || candidate.operator !== "=") return;
|
|
69380
|
-
if (serializeReferenceKey({
|
|
69381
|
-
node: candidate.left,
|
|
69382
|
-
scopes: context.scopes
|
|
69383
|
-
}) === refKey && serializeReferenceKey({
|
|
69384
|
-
node: candidate.right,
|
|
69385
|
-
scopes: context.scopes
|
|
69386
|
-
}) === tokenKey) candidates.push(candidate);
|
|
69387
|
-
});
|
|
69388
|
-
const claim = candidates.find((candidate) => truthySets.some((truthySet) => claimPrecedesTruthySet(candidate, truthySet, firstRiskyAwait, functionNode, context)));
|
|
69389
|
-
if (!claim) return null;
|
|
69390
|
-
let didFindOtherWrite = false;
|
|
69391
|
-
walkAst(getOwningFunction(functionNode), (candidate) => {
|
|
69392
|
-
if (didFindOtherWrite || candidate === claim) return;
|
|
69393
|
-
const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
|
|
69394
|
-
if (writeTarget && serializeReferenceKey({
|
|
69395
|
-
node: writeTarget,
|
|
69396
|
-
scopes: context.scopes
|
|
69397
|
-
}) === refKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindOtherWrite = true;
|
|
69398
|
-
});
|
|
69399
|
-
return didFindOtherWrite ? null : claim;
|
|
69400
|
-
};
|
|
69401
|
-
const isClaimedOwnershipComparison = (test, expectedMode, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
|
|
69402
|
-
const comparison = getAsyncOwnershipComparison(test, context);
|
|
69403
|
-
return Boolean(comparison && comparison.mode === expectedMode && findOwnershipClaim(comparison, functionNode, truthySets, firstRiskyAwait, resetNode, context));
|
|
69404
|
-
};
|
|
69405
|
-
const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, acceptedAssignments, context) => {
|
|
68097
|
+
const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, acceptedCleanupAssignments, context) => {
|
|
69406
68098
|
let didFindOtherWrite = false;
|
|
69407
68099
|
walkAst(effectCallback, (candidate) => {
|
|
69408
68100
|
if (didFindOtherWrite) return false;
|
|
@@ -69410,7 +68102,7 @@ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, accepted
|
|
|
69410
68102
|
if (serializeReferenceKey({
|
|
69411
68103
|
node: candidate.left,
|
|
69412
68104
|
scopes: context.scopes
|
|
69413
|
-
}) === guardKey && !
|
|
68105
|
+
}) === guardKey && !acceptedCleanupAssignments.has(candidate)) {
|
|
69414
68106
|
didFindOtherWrite = true;
|
|
69415
68107
|
return false;
|
|
69416
68108
|
}
|
|
@@ -69426,103 +68118,48 @@ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, accepted
|
|
|
69426
68118
|
});
|
|
69427
68119
|
return didFindOtherWrite;
|
|
69428
68120
|
};
|
|
69429
|
-
const
|
|
69430
|
-
const acceptedAssignments = /* @__PURE__ */ new Set();
|
|
69431
|
-
for (const cleanupFunction of collectReturnedCleanupFunctions(effectCallback, context.scopes)) walkOwnFunctionScope(cleanupFunction, (cleanupNode) => {
|
|
69432
|
-
const assignedValue = isNodeOfType(cleanupNode, "AssignmentExpression") ? stripParenExpression(cleanupNode.right) : null;
|
|
69433
|
-
if (!isNodeOfType(cleanupNode, "AssignmentExpression") || cleanupNode.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== false || serializeReferenceKey({
|
|
69434
|
-
node: cleanupNode.left,
|
|
69435
|
-
scopes: context.scopes
|
|
69436
|
-
}) !== guardKey || !isUnconditionallyExecutedWithinFunction(cleanupNode, cleanupFunction, context)) return;
|
|
69437
|
-
acceptedAssignments.add(cleanupNode);
|
|
69438
|
-
});
|
|
69439
|
-
if (acceptedAssignments.size === 0) return null;
|
|
69440
|
-
walkOwnFunctionScope(effectCallback, (effectNode) => {
|
|
69441
|
-
const assignedValue = isNodeOfType(effectNode, "AssignmentExpression") ? stripParenExpression(effectNode.right) : null;
|
|
69442
|
-
if (isNodeOfType(effectNode, "AssignmentExpression") && effectNode.operator === "=" && isNodeOfType(assignedValue, "Literal") && assignedValue.value === true && serializeReferenceKey({
|
|
69443
|
-
node: effectNode.left,
|
|
69444
|
-
scopes: context.scopes
|
|
69445
|
-
}) === guardKey && isUnconditionallyExecutedWithinFunction(effectNode, effectCallback, context)) acceptedAssignments.add(effectNode);
|
|
69446
|
-
});
|
|
69447
|
-
return acceptedAssignments;
|
|
69448
|
-
};
|
|
69449
|
-
const isEffectCallback = (node, context) => {
|
|
69450
|
-
const callbackRoot = findTransparentExpressionRoot(node);
|
|
69451
|
-
const callbackCall = callbackRoot.parent;
|
|
69452
|
-
return Boolean(callbackCall && isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments[0] === callbackRoot && isReactApiCall(callbackCall, EFFECT_HOOK_NAMES$6, context.scopes, {
|
|
69453
|
-
allowGlobalReactNamespace: true,
|
|
69454
|
-
allowUnboundBareCalls: true
|
|
69455
|
-
}));
|
|
69456
|
-
};
|
|
69457
|
-
const isCleanupBackedLifecycleGuard = (guardExpression, functionNode, context) => {
|
|
69458
|
-
const guardKey = serializeReferenceKey({
|
|
69459
|
-
node: guardExpression,
|
|
69460
|
-
scopes: context.scopes
|
|
69461
|
-
});
|
|
69462
|
-
if (!guardKey || !isInitiallyActiveLifecycleGuard(guardExpression, context)) return false;
|
|
69463
|
-
let ownerFunction = functionNode.parent;
|
|
69464
|
-
while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
|
|
69465
|
-
if (!ownerFunction) return false;
|
|
69466
|
-
const effectCallbacks = [];
|
|
69467
|
-
if (isEffectCallback(ownerFunction, context)) effectCallbacks.push(ownerFunction);
|
|
69468
|
-
walkOwnFunctionScope(ownerFunction, (candidate) => {
|
|
69469
|
-
if (!isNodeOfType(candidate, "CallExpression")) return;
|
|
69470
|
-
if (!isReactApiCall(candidate, EFFECT_HOOK_NAMES$6, context.scopes, {
|
|
69471
|
-
allowGlobalReactNamespace: true,
|
|
69472
|
-
allowUnboundBareCalls: true
|
|
69473
|
-
})) return;
|
|
69474
|
-
const effectCallback = candidate.arguments[0];
|
|
69475
|
-
if (effectCallback && isFunctionLike$1(effectCallback)) effectCallbacks.push(effectCallback);
|
|
69476
|
-
});
|
|
69477
|
-
const acceptedAssignments = /* @__PURE__ */ new Set();
|
|
69478
|
-
for (const effectCallback of effectCallbacks) {
|
|
69479
|
-
const effectAssignments = collectCleanupBackedLifecycleAssignments(effectCallback, guardKey, context);
|
|
69480
|
-
if (!effectAssignments) continue;
|
|
69481
|
-
for (const assignment of effectAssignments) acceptedAssignments.add(assignment);
|
|
69482
|
-
}
|
|
69483
|
-
return Boolean(acceptedAssignments.size > 0 && !hasLifecycleGuardWriteOutsideCleanup(ownerFunction, guardKey, acceptedAssignments, context));
|
|
69484
|
-
};
|
|
69485
|
-
const collectLogicalOperands = (expression, operator) => {
|
|
69486
|
-
const stripped = stripParenExpression(expression);
|
|
69487
|
-
if (isNodeOfType(stripped, "LogicalExpression") && stripped.operator === operator) return [...collectLogicalOperands(stripped.left, operator), ...collectLogicalOperands(stripped.right, operator)];
|
|
69488
|
-
return [stripped];
|
|
69489
|
-
};
|
|
69490
|
-
const collectFinalizerGuardExpressions = (resetNode, protectingTry) => {
|
|
69491
|
-
const positive = [];
|
|
69492
|
-
const negative = [];
|
|
68121
|
+
const isResetGuardedByCleanupBackedLifecycle = (resetNode, functionNode, context) => {
|
|
69493
68122
|
let child = resetNode;
|
|
69494
68123
|
let cursor = resetNode.parent;
|
|
69495
|
-
|
|
69496
|
-
|
|
69497
|
-
|
|
69498
|
-
|
|
69499
|
-
|
|
69500
|
-
|
|
69501
|
-
|
|
69502
|
-
|
|
69503
|
-
|
|
69504
|
-
|
|
69505
|
-
|
|
69506
|
-
negative.push(...collectLogicalOperands(statement.test, "||"));
|
|
69507
|
-
}
|
|
69508
|
-
} else if (isNodeOfType(cursor, "SwitchCase") || isNodeOfType(cursor, "ConditionalExpression") || isNodeOfType(cursor, "ForStatement") || isNodeOfType(cursor, "ForInStatement") || isNodeOfType(cursor, "ForOfStatement") || isNodeOfType(cursor, "WhileStatement") || isNodeOfType(cursor, "DoWhileStatement")) return null;
|
|
68124
|
+
let guardKey = null;
|
|
68125
|
+
let guardExpression = null;
|
|
68126
|
+
while (cursor && cursor !== functionNode) {
|
|
68127
|
+
if (isNodeOfType(cursor, "IfStatement") && cursor.consequent === child && cursor.alternate === null) {
|
|
68128
|
+
guardExpression = cursor.test;
|
|
68129
|
+
guardKey = serializeReferenceKey({
|
|
68130
|
+
node: cursor.test,
|
|
68131
|
+
scopes: context.scopes
|
|
68132
|
+
});
|
|
68133
|
+
break;
|
|
68134
|
+
}
|
|
69509
68135
|
child = cursor;
|
|
69510
68136
|
cursor = cursor.parent ?? null;
|
|
69511
68137
|
}
|
|
69512
|
-
|
|
69513
|
-
|
|
69514
|
-
|
|
69515
|
-
|
|
69516
|
-
|
|
69517
|
-
const
|
|
69518
|
-
|
|
69519
|
-
|
|
69520
|
-
|
|
69521
|
-
|
|
69522
|
-
|
|
69523
|
-
const
|
|
69524
|
-
|
|
69525
|
-
|
|
68138
|
+
if (!guardKey || !guardExpression || !isInitiallyActiveLifecycleGuard(guardExpression, context)) return false;
|
|
68139
|
+
cursor = functionNode.parent;
|
|
68140
|
+
while (cursor) {
|
|
68141
|
+
if (isFunctionLike$1(cursor)) {
|
|
68142
|
+
const callbackRoot = findTransparentExpressionRoot(cursor);
|
|
68143
|
+
const callbackCall = callbackRoot.parent;
|
|
68144
|
+
if (Boolean(callbackCall && isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments[0] === callbackRoot && isReactApiCall(callbackCall, EFFECT_HOOK_NAMES$6, context.scopes, {
|
|
68145
|
+
allowGlobalReactNamespace: true,
|
|
68146
|
+
allowUnboundBareCalls: true
|
|
68147
|
+
}))) {
|
|
68148
|
+
const acceptedCleanupAssignments = /* @__PURE__ */ new Set();
|
|
68149
|
+
for (const cleanupFunction of collectReturnedCleanupFunctions(cursor, context.scopes)) walkOwnFunctionScope(cleanupFunction, (cleanupNode) => {
|
|
68150
|
+
const assignedValue = isNodeOfType(cleanupNode, "AssignmentExpression") ? stripParenExpression(cleanupNode.right) : null;
|
|
68151
|
+
if (!isNodeOfType(cleanupNode, "AssignmentExpression") || cleanupNode.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== false || serializeReferenceKey({
|
|
68152
|
+
node: cleanupNode.left,
|
|
68153
|
+
scopes: context.scopes
|
|
68154
|
+
}) !== guardKey || !isUnconditionallyExecutedWithinFunction(cleanupNode, cleanupFunction, context)) return;
|
|
68155
|
+
acceptedCleanupAssignments.add(cleanupNode);
|
|
68156
|
+
});
|
|
68157
|
+
if (acceptedCleanupAssignments.size > 0 && !hasLifecycleGuardWriteOutsideCleanup(cursor, guardKey, acceptedCleanupAssignments, context)) return true;
|
|
68158
|
+
}
|
|
68159
|
+
}
|
|
68160
|
+
cursor = cursor.parent ?? null;
|
|
68161
|
+
}
|
|
68162
|
+
return false;
|
|
69526
68163
|
};
|
|
69527
68164
|
const isAwaitInsideProtectedTry = (awaitNode, tryStatement) => {
|
|
69528
68165
|
let child = awaitNode;
|
|
@@ -69628,13 +68265,7 @@ const analyzeFunction = (functionNode, context) => {
|
|
|
69628
68265
|
const exceptionallyProtectedAwaits = collectExceptionallyProtectedAwaits(awaitSites, calls);
|
|
69629
68266
|
const riskyAwaitsWithTruthySet = awaitSites.filter((awaitSite) => rejectingAwaitNodes.has(awaitSite.node) && !exceptionallyProtectedAwaits.has(awaitSite.node) && truthySets.some((truthySet) => truthySet.start < awaitSite.start && !areOnExclusiveBranches(truthySet.node, awaitSite.node, functionNode)));
|
|
69630
68267
|
if (riskyAwaitsWithTruthySet.length === 0) continue;
|
|
69631
|
-
const conditionalExceptionalResets = calls.filter((call) =>
|
|
69632
|
-
if (call.value || call.context === "plain" || call.isUnconditional || call.protectingTry === null) return false;
|
|
69633
|
-
const protectingTry = call.protectingTry;
|
|
69634
|
-
if (!isInsideTryFinalizer(call.node, protectingTry)) return true;
|
|
69635
|
-
const firstRiskyAwait = riskyAwaitsWithTruthySet.find((awaitSite) => isAwaitInsideProtectedTry(awaitSite.node, protectingTry));
|
|
69636
|
-
return !(firstRiskyAwait && isFinalizerResetProvablyGuarded(call.node, protectingTry, functionNode, truthySets, firstRiskyAwait, context));
|
|
69637
|
-
});
|
|
68268
|
+
const conditionalExceptionalResets = calls.filter((call) => !call.value && call.context !== "plain" && !call.isUnconditional && call.protectingTry !== null && !(isInsideTryFinalizer(call.node, call.protectingTry) && isResetGuardedByCleanupBackedLifecycle(call.node, functionNode, context)));
|
|
69638
68269
|
for (const reset of conditionalExceptionalResets) {
|
|
69639
68270
|
const catchHandler = reset.protectingTry?.handler;
|
|
69640
68271
|
if (catchHandler && !catchHandlerCanBypassReset(catchHandler, functionNode, setterKey, context, false)) continue;
|
|
@@ -75506,29 +74137,6 @@ const doesPredicateTruthRequireMatch = (matchCall, predicateFunction) => {
|
|
|
75506
74137
|
}
|
|
75507
74138
|
return !isNegated && predicateFunction.body === child;
|
|
75508
74139
|
};
|
|
75509
|
-
const doesPredicateReturnNormalizedMatch = (matchCall, predicateFunction) => {
|
|
75510
|
-
if (!isFunctionLike$1(predicateFunction) || !isNodeOfType(predicateFunction.body, "BlockStatement") || predicateFunction.body.body.length !== 2 || !isNodeOfType(predicateFunction.body.body[0], "VariableDeclaration")) return false;
|
|
75511
|
-
const returnStatement = predicateFunction.body.body[1];
|
|
75512
|
-
if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
|
|
75513
|
-
let negationCount = 0;
|
|
75514
|
-
let expression = matchCall;
|
|
75515
|
-
let parent = expression.parent ?? null;
|
|
75516
|
-
while (parent && parent !== returnStatement) {
|
|
75517
|
-
if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") {
|
|
75518
|
-
negationCount += 1;
|
|
75519
|
-
expression = parent;
|
|
75520
|
-
parent = parent.parent ?? null;
|
|
75521
|
-
continue;
|
|
75522
|
-
}
|
|
75523
|
-
if (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) || isNodeOfType(parent, "ChainExpression")) {
|
|
75524
|
-
expression = parent;
|
|
75525
|
-
parent = parent.parent ?? null;
|
|
75526
|
-
continue;
|
|
75527
|
-
}
|
|
75528
|
-
return false;
|
|
75529
|
-
}
|
|
75530
|
-
return parent === returnStatement && returnStatement.argument === expression && negationCount % 2 === 0;
|
|
75531
|
-
};
|
|
75532
74140
|
const isStringTypeofGuardForPath = (test, expectedPath) => {
|
|
75533
74141
|
const target = stripParenExpression(test);
|
|
75534
74142
|
if (!isNodeOfType(target, "BinaryExpression") || target.operator !== "===") return false;
|
|
@@ -75569,26 +74177,6 @@ const pathUsesOptionalAccess = (node) => {
|
|
|
75569
74177
|
current = current.object;
|
|
75570
74178
|
}
|
|
75571
74179
|
};
|
|
75572
|
-
const getNormalizedClassNameRoot = (expression) => {
|
|
75573
|
-
const conditional = stripParenExpression(expression);
|
|
75574
|
-
if (!isNodeOfType(conditional, "ConditionalExpression")) return null;
|
|
75575
|
-
const consequent = stripParenExpression(conditional.consequent);
|
|
75576
|
-
const rootIdentifier = getRootIdentifier(consequent);
|
|
75577
|
-
if (!rootIdentifier || receiverPathKey(consequent) !== `${rootIdentifier.name}.className`) return null;
|
|
75578
|
-
const test = stripParenExpression(conditional.test);
|
|
75579
|
-
if (!isNodeOfType(test, "BinaryExpression") || test.operator !== "===") return null;
|
|
75580
|
-
const testOperands = [test.left, test.right].map((operand) => stripParenExpression(operand));
|
|
75581
|
-
const typeofOperand = testOperands.find((operand) => isNodeOfType(operand, "UnaryExpression"));
|
|
75582
|
-
const stringOperand = testOperands.find((operand) => isNodeOfType(operand, "Literal"));
|
|
75583
|
-
if (!typeofOperand || !isNodeOfType(typeofOperand, "UnaryExpression") || typeofOperand.operator !== "typeof" || receiverPathKey(typeofOperand.argument) !== `${rootIdentifier.name}.className` || !stringOperand || !isNodeOfType(stringOperand, "Literal") || stringOperand.value !== "string") return null;
|
|
75584
|
-
const alternate = stripParenExpression(conditional.alternate);
|
|
75585
|
-
if (!isNodeOfType(alternate, "LogicalExpression") || alternate.operator !== "??") return null;
|
|
75586
|
-
const fallback = stripParenExpression(alternate.right);
|
|
75587
|
-
const attributeCall = stripParenExpression(alternate.left);
|
|
75588
|
-
if (!isNodeOfType(fallback, "Literal") || fallback.value !== "" || !isNodeOfType(attributeCall, "CallExpression") || !isNodeOfType(attributeCall.callee, "MemberExpression") || getStaticPropertyName(attributeCall.callee) !== "getAttribute" || receiverPathKey(attributeCall.callee.object) !== rootIdentifier.name) return null;
|
|
75589
|
-
const attributeName = attributeCall.arguments[0] ? stripParenExpression(attributeCall.arguments[0]) : null;
|
|
75590
|
-
return attributeName && isNodeOfType(attributeName, "Literal") && attributeName.value === "class" ? rootIdentifier : null;
|
|
75591
|
-
};
|
|
75592
74180
|
const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
|
|
75593
74181
|
const resultIdentifier = getRootIdentifier(matchReceiver);
|
|
75594
74182
|
const resultPath = receiverPathKey(matchReceiver);
|
|
@@ -75597,17 +74185,11 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
|
|
|
75597
74185
|
if (!isDirectFinderMatchReturn(assertion) && !isOptionalResultPath) return false;
|
|
75598
74186
|
const resultSymbol = context.scopes.symbolFor(resultIdentifier);
|
|
75599
74187
|
const initializer = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
|
|
75600
|
-
if (resultSymbol?.kind !== "const" || !initializer) return false;
|
|
75601
|
-
const finderCall = isNodeOfType(initializer, "CallExpression") ? initializer : isNodeOfType(initializer, "ConditionalExpression") ? (() => {
|
|
75602
|
-
const alternate = stripParenExpression(initializer.alternate);
|
|
75603
|
-
const consequent = stripParenExpression(initializer.consequent);
|
|
75604
|
-
return (isNodeOfType(alternate, "Literal") && alternate.value === null || isNodeOfType(alternate, "Identifier") && alternate.name === "undefined" && context.scopes.isGlobalReference(alternate)) && isNodeOfType(consequent, "CallExpression") ? consequent : null;
|
|
75605
|
-
})() : null;
|
|
75606
|
-
if (!finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
|
|
74188
|
+
if (resultSymbol?.kind !== "const" || !initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
75607
74189
|
if (!isOptionalResultPath && !isImmediatelyGuardedFinderResult(assertion, resultSymbol, resultPath, context)) return false;
|
|
75608
|
-
const finderCallee = stripParenExpression(
|
|
74190
|
+
const finderCallee = stripParenExpression(initializer.callee);
|
|
75609
74191
|
if (!isNodeOfType(finderCallee, "Identifier") || !(context.scopes.symbolFor(finderCallee)?.kind === "import" && getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) === "findUpUntil" || finderCallee.name === "findUpUntil" && context.scopes.isGlobalReference(finderCallee))) return false;
|
|
75610
|
-
const predicateArgument =
|
|
74192
|
+
const predicateArgument = initializer.arguments[1];
|
|
75611
74193
|
if (!predicateArgument) return false;
|
|
75612
74194
|
const predicateFunction = resolveExactLocalFunction(predicateArgument, context.scopes);
|
|
75613
74195
|
if (!predicateFunction || !isFunctionLike$1(predicateFunction)) return false;
|
|
@@ -75631,43 +74213,6 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
|
|
|
75631
74213
|
});
|
|
75632
74214
|
return didProveMatch;
|
|
75633
74215
|
};
|
|
75634
|
-
const isMatchProvenByNormalizedFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
|
|
75635
|
-
const normalizedReceiver = stripParenExpression(matchReceiver);
|
|
75636
|
-
if (!isNodeOfType(normalizedReceiver, "Identifier")) return false;
|
|
75637
|
-
const normalizedReceiverSymbol = context.scopes.symbolFor(normalizedReceiver);
|
|
75638
|
-
const normalizedReceiverInitializer = normalizedReceiverSymbol?.initializer ? stripParenExpression(normalizedReceiverSymbol.initializer) : null;
|
|
75639
|
-
const resultIdentifier = normalizedReceiverInitializer ? getNormalizedClassNameRoot(normalizedReceiverInitializer) : null;
|
|
75640
|
-
if (normalizedReceiverSymbol?.kind !== "const" || normalizedReceiverSymbol.references.some((reference) => reference.flag !== "read") || !resultIdentifier) return false;
|
|
75641
|
-
const resultSymbol = context.scopes.symbolFor(resultIdentifier);
|
|
75642
|
-
const finderCall = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
|
|
75643
|
-
if (resultSymbol?.kind !== "const" || resultSymbol.references.some((reference) => reference.flag !== "read") || !finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
|
|
75644
|
-
const finderCallee = stripParenExpression(finderCall.callee);
|
|
75645
|
-
if (!isNodeOfType(finderCallee, "Identifier") || context.scopes.symbolFor(finderCallee)?.kind !== "import" || getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) !== "findUpUntil") return false;
|
|
75646
|
-
if (!isPresenceProvenBeforeNode(assertion, (test) => {
|
|
75647
|
-
const expression = stripParenExpression(test);
|
|
75648
|
-
return isNodeOfType(expression, "Identifier") && context.scopes.symbolFor(expression)?.id === resultSymbol.id;
|
|
75649
|
-
})) return false;
|
|
75650
|
-
const predicateArgument = finderCall.arguments[1];
|
|
75651
|
-
const predicateFunction = predicateArgument ? resolveExactLocalFunction(predicateArgument, context.scopes) : null;
|
|
75652
|
-
if (!predicateFunction || !isFunctionLike$1(predicateFunction) || predicateFunction.async || predicateFunction.generator) return false;
|
|
75653
|
-
const predicateParameter = predicateFunction.params[0];
|
|
75654
|
-
if (!isNodeOfType(predicateParameter, "Identifier")) return false;
|
|
75655
|
-
let didProveNormalizedMatch = false;
|
|
75656
|
-
walkAst(predicateFunction.body, (child) => {
|
|
75657
|
-
if (didProveNormalizedMatch || isFunctionLike$1(child)) return false;
|
|
75658
|
-
if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || getStaticPropertyName(child.callee) !== "match" || !child.arguments[0] || !areRegexPatternsEquivalent(child.arguments[0], assertedPattern, context) || !doesPredicateTruthRequireMatch(child, predicateFunction) && !doesPredicateReturnNormalizedMatch(child, predicateFunction)) return;
|
|
75659
|
-
const predicateReceiver = stripParenExpression(child.callee.object);
|
|
75660
|
-
if (!isNodeOfType(predicateReceiver, "Identifier")) return;
|
|
75661
|
-
const predicateReceiverSymbol = context.scopes.symbolFor(predicateReceiver);
|
|
75662
|
-
const predicateReceiverInitializer = predicateReceiverSymbol?.initializer ? stripParenExpression(predicateReceiverSymbol.initializer) : null;
|
|
75663
|
-
const predicateRoot = predicateReceiverInitializer ? getNormalizedClassNameRoot(predicateReceiverInitializer) : null;
|
|
75664
|
-
if (predicateReceiverSymbol?.kind === "const" && predicateReceiverSymbol.references.every((reference) => reference.flag === "read") && predicateRoot?.name === predicateParameter.name) {
|
|
75665
|
-
didProveNormalizedMatch = true;
|
|
75666
|
-
return false;
|
|
75667
|
-
}
|
|
75668
|
-
});
|
|
75669
|
-
return didProveNormalizedMatch;
|
|
75670
|
-
};
|
|
75671
74216
|
const scopeProvesFindMatch = (assertion, findReceiver, findPredicate, context) => {
|
|
75672
74217
|
if (!isStablePredicate(findPredicate, context)) return false;
|
|
75673
74218
|
return isPresenceProvenBeforeNode(assertion, (test) => testPositivelyContainsCall(test, (call) => {
|
|
@@ -75768,123 +74313,6 @@ const isEnsureThenFind = (assertion, findReceiver, findPredicate) => {
|
|
|
75768
74313
|
}
|
|
75769
74314
|
return false;
|
|
75770
74315
|
};
|
|
75771
|
-
const getReceiverRootIdentifier = (node) => {
|
|
75772
|
-
let target = stripParenExpression(node);
|
|
75773
|
-
while (isNodeOfType(target, "MemberExpression")) target = stripParenExpression(target.object);
|
|
75774
|
-
return isNodeOfType(target, "Identifier") ? target : null;
|
|
75775
|
-
};
|
|
75776
|
-
const getReceiverStatePath = (node) => {
|
|
75777
|
-
const target = stripParenExpression(node);
|
|
75778
|
-
if (isNodeOfType(target, "Identifier")) return target.name;
|
|
75779
|
-
if (!isNodeOfType(target, "MemberExpression")) return null;
|
|
75780
|
-
const objectPath = getReceiverStatePath(target.object);
|
|
75781
|
-
if (!objectPath) return null;
|
|
75782
|
-
return `${objectPath}.${getStaticPropertyName(target) ?? "*"}`;
|
|
75783
|
-
};
|
|
75784
|
-
const doesReceiverStateChangeBeforeAssertion = (ownerFunction, receiver, startOffset, assertion, context) => {
|
|
75785
|
-
const receiverRoot = getReceiverRootIdentifier(receiver);
|
|
75786
|
-
const receiverSymbol = receiverRoot ? context.scopes.symbolFor(receiverRoot) : null;
|
|
75787
|
-
const receiverPath = getReceiverStatePath(receiver);
|
|
75788
|
-
if (!receiverRoot || !receiverSymbol || !receiverPath) return true;
|
|
75789
|
-
const receiverAliasPaths = new Map([[receiverSymbol.id, receiverRoot.name]]);
|
|
75790
|
-
let didAddAlias = true;
|
|
75791
|
-
while (didAddAlias) {
|
|
75792
|
-
didAddAlias = false;
|
|
75793
|
-
walkAst(ownerFunction, (child) => {
|
|
75794
|
-
if (child !== ownerFunction && isFunctionLike$1(child)) return false;
|
|
75795
|
-
if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier") || !child.init) return;
|
|
75796
|
-
const initializer = stripParenExpression(child.init);
|
|
75797
|
-
if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return;
|
|
75798
|
-
const initializerRoot = getReceiverRootIdentifier(initializer);
|
|
75799
|
-
const initializerSymbol = initializerRoot ? context.scopes.symbolFor(initializerRoot) : null;
|
|
75800
|
-
const initializerBasePath = initializerSymbol ? receiverAliasPaths.get(initializerSymbol.id) : null;
|
|
75801
|
-
const initializerPath = getReceiverStatePath(initializer);
|
|
75802
|
-
if (!initializerRoot || !initializerBasePath || !initializerPath) return;
|
|
75803
|
-
const aliasSymbol = context.scopes.symbolFor(child.id);
|
|
75804
|
-
if (aliasSymbol && !receiverAliasPaths.has(aliasSymbol.id)) {
|
|
75805
|
-
const initializerSuffix = initializerPath.slice(initializerRoot.name.length);
|
|
75806
|
-
receiverAliasPaths.set(aliasSymbol.id, `${initializerBasePath}${initializerSuffix}`);
|
|
75807
|
-
didAddAlias = true;
|
|
75808
|
-
}
|
|
75809
|
-
});
|
|
75810
|
-
}
|
|
75811
|
-
let didChangeReceiverState = false;
|
|
75812
|
-
walkAst(ownerFunction, (child) => {
|
|
75813
|
-
if (didChangeReceiverState) return false;
|
|
75814
|
-
if (child !== ownerFunction && isFunctionLike$1(child)) return false;
|
|
75815
|
-
if (child.range[0] <= startOffset || child.range[0] >= assertion.range[0]) return;
|
|
75816
|
-
if (isNodeOfType(child, "CallExpression")) {
|
|
75817
|
-
didChangeReceiverState = true;
|
|
75818
|
-
return false;
|
|
75819
|
-
}
|
|
75820
|
-
const mutationTarget = isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete" ? stripParenExpression(isNodeOfType(child, "AssignmentExpression") ? child.left : child.argument) : null;
|
|
75821
|
-
const mutationRoot = mutationTarget ? getReceiverRootIdentifier(mutationTarget) : null;
|
|
75822
|
-
const mutationSymbol = mutationRoot ? context.scopes.symbolFor(mutationRoot) : null;
|
|
75823
|
-
const mutationBasePath = mutationSymbol ? receiverAliasPaths.get(mutationSymbol.id) : null;
|
|
75824
|
-
const mutationPath = mutationTarget ? getReceiverStatePath(mutationTarget) : null;
|
|
75825
|
-
if (mutationRoot && mutationBasePath && mutationPath) {
|
|
75826
|
-
const canonicalMutationPath = `${mutationBasePath}${mutationPath.slice(mutationRoot.name.length)}`;
|
|
75827
|
-
if (canonicalMutationPath !== receiverPath && !canonicalMutationPath.startsWith(`${receiverPath}.`) && !receiverPath.startsWith(`${canonicalMutationPath}.`)) return;
|
|
75828
|
-
didChangeReceiverState = true;
|
|
75829
|
-
return false;
|
|
75830
|
-
}
|
|
75831
|
-
});
|
|
75832
|
-
return didChangeReceiverState;
|
|
75833
|
-
};
|
|
75834
|
-
const isFindProvenByGuardedMaximum = (assertion, findReceiver, findPredicate, context) => {
|
|
75835
|
-
const findLookup = findEqualityLookupParts(findPredicate);
|
|
75836
|
-
const maximumIdentifier = findLookup ? stripParenExpression(findLookup.comparedValue) : null;
|
|
75837
|
-
if (!findLookup || !maximumIdentifier || !isNodeOfType(maximumIdentifier, "Identifier")) return false;
|
|
75838
|
-
const maximumSymbol = context.scopes.symbolFor(maximumIdentifier);
|
|
75839
|
-
const maximumInitializer = maximumSymbol?.initializer ? stripParenExpression(maximumSymbol.initializer) : null;
|
|
75840
|
-
if (maximumSymbol?.kind !== "const" || maximumSymbol.references.some((reference) => reference.flag !== "read") || !maximumInitializer || !isNodeOfType(maximumInitializer, "CallExpression") || !isNodeOfType(maximumInitializer.callee, "MemberExpression") || getStaticPropertyName(maximumInitializer.callee) !== "reduce") return false;
|
|
75841
|
-
const filterCall = stripParenExpression(maximumInitializer.callee.object);
|
|
75842
|
-
if (!isNodeOfType(filterCall, "CallExpression") || !isNodeOfType(filterCall.callee, "MemberExpression") || getStaticPropertyName(filterCall.callee) !== "filter" || !areNodesLooselyEqual(filterCall.callee.object, findReceiver)) return false;
|
|
75843
|
-
const filterPredicate = filterCall.arguments[0] ? stripParenExpression(filterCall.arguments[0]) : null;
|
|
75844
|
-
if (!filterPredicate || !isStablePredicate(filterPredicate, context)) return false;
|
|
75845
|
-
const reducerArgument = maximumInitializer.arguments[0] ? stripParenExpression(maximumInitializer.arguments[0]) : null;
|
|
75846
|
-
const reducerFunction = reducerArgument ? resolveExactLocalFunction(reducerArgument, context.scopes) : null;
|
|
75847
|
-
const initialValue = maximumInitializer.arguments[1] ? stripParenExpression(maximumInitializer.arguments[1]) : null;
|
|
75848
|
-
if (!reducerFunction || !isFunctionLike$1(reducerFunction) || reducerFunction.async || reducerFunction.generator || !initialValue || !isNodeOfType(initialValue, "Literal") || typeof initialValue.value !== "number") return false;
|
|
75849
|
-
const accumulatorParameter = reducerFunction.params[0];
|
|
75850
|
-
const itemParameter = reducerFunction.params[1];
|
|
75851
|
-
const reducerBody = singleExpressionPredicateBody(reducerFunction);
|
|
75852
|
-
if (!isNodeOfType(accumulatorParameter, "Identifier") || !isNodeOfType(itemParameter, "Identifier") || !reducerBody || !isNodeOfType(reducerBody, "CallExpression") || !isNodeOfType(reducerBody.callee, "MemberExpression") || getStaticPropertyName(reducerBody.callee) !== "max") return false;
|
|
75853
|
-
const mathReceiver = stripParenExpression(reducerBody.callee.object);
|
|
75854
|
-
if (!isNodeOfType(mathReceiver, "Identifier") || mathReceiver.name !== "Math" || !context.scopes.isGlobalReference(mathReceiver) || reducerBody.arguments.length !== 2) return false;
|
|
75855
|
-
const accumulatorArgument = reducerBody.arguments.find((argument) => {
|
|
75856
|
-
const expression = stripParenExpression(argument);
|
|
75857
|
-
return isNodeOfType(expression, "Identifier") && expression.name === accumulatorParameter.name;
|
|
75858
|
-
});
|
|
75859
|
-
const itemMemberArgument = reducerBody.arguments.find((argument) => {
|
|
75860
|
-
const expression = stripParenExpression(argument);
|
|
75861
|
-
const rootIdentifier = getRootIdentifier(expression);
|
|
75862
|
-
return isNodeOfType(expression, "MemberExpression") && rootIdentifier?.name === itemParameter.name && receiverPathKey(expression)?.slice(itemParameter.name.length + 1) === findLookup.propertyName;
|
|
75863
|
-
});
|
|
75864
|
-
if (!accumulatorArgument || !itemMemberArgument) return false;
|
|
75865
|
-
if (!receiverPathKey(findReceiver)) return false;
|
|
75866
|
-
let ownerFunction = assertion.parent ?? null;
|
|
75867
|
-
while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
|
|
75868
|
-
if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
|
|
75869
|
-
const maximumEnd = maximumInitializer.range[1];
|
|
75870
|
-
if (doesReceiverStateChangeBeforeAssertion(ownerFunction.body, findReceiver, maximumEnd, assertion, context)) return false;
|
|
75871
|
-
return isPresenceProvenBeforeNode(assertion, (test) => {
|
|
75872
|
-
const comparison = stripParenExpression(test);
|
|
75873
|
-
if (!isNodeOfType(comparison, "BinaryExpression")) return false;
|
|
75874
|
-
return [[
|
|
75875
|
-
comparison.left,
|
|
75876
|
-
comparison.right,
|
|
75877
|
-
comparison.operator
|
|
75878
|
-
], [
|
|
75879
|
-
comparison.right,
|
|
75880
|
-
comparison.left,
|
|
75881
|
-
comparison.operator === "<" ? ">" : comparison.operator === ">" ? "<" : comparison.operator
|
|
75882
|
-
]].some(([candidateMaximum, candidateInitial, operator]) => {
|
|
75883
|
-
const candidateMaximumIdentifier = stripParenExpression(candidateMaximum);
|
|
75884
|
-
return operator === ">" && isNodeOfType(candidateMaximumIdentifier, "Identifier") && context.scopes.symbolFor(candidateMaximumIdentifier)?.id === maximumSymbol.id && areNodesLooselyEqual(stripParenExpression(candidateInitial), initialValue);
|
|
75885
|
-
});
|
|
75886
|
-
});
|
|
75887
|
-
};
|
|
75888
74316
|
const isDefinitelyNonNullishMapValue = (value) => {
|
|
75889
74317
|
if (!value) return false;
|
|
75890
74318
|
const expression = stripParenExpression(value);
|
|
@@ -75908,15 +74336,15 @@ const unwrapFalseBooleanGuard = (test) => {
|
|
|
75908
74336
|
};
|
|
75909
74337
|
const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
|
|
75910
74338
|
const stableLookupKey = stripParenExpression(lookupKey);
|
|
75911
|
-
|
|
75912
|
-
const lookupKeySymbol = isNodeOfType(stableLookupKey, "Identifier") ? context.scopes.symbolFor(stableLookupKey) : lookupKeyRoot ? context.scopes.symbolFor(lookupKeyRoot) : null;
|
|
75913
|
-
if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal") && (!isNodeOfType(stableLookupKey, "MemberExpression") || !lookupKeyRoot || lookupKeySymbol?.kind !== "const")) return false;
|
|
74339
|
+
if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal")) return false;
|
|
75914
74340
|
const receiverSymbol = context.scopes.symbolFor(receiver);
|
|
75915
74341
|
if (!receiverSymbol) return false;
|
|
75916
74342
|
const receiverMatches = (candidate) => {
|
|
75917
74343
|
const target = stripParenExpression(candidate);
|
|
75918
74344
|
return isNodeOfType(target, "Identifier") && context.scopes.symbolFor(target)?.id === receiverSymbol.id;
|
|
75919
74345
|
};
|
|
74346
|
+
const lookupKeyExpression = stripParenExpression(lookupKey);
|
|
74347
|
+
const lookupKeySymbol = isNodeOfType(lookupKeyExpression, "Identifier") ? context.scopes.symbolFor(lookupKeyExpression) : null;
|
|
75920
74348
|
let child = assertion;
|
|
75921
74349
|
let ancestor = assertion.parent ?? null;
|
|
75922
74350
|
while (ancestor && !isFunctionLike$1(ancestor)) {
|
|
@@ -75936,7 +74364,7 @@ const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
|
|
|
75936
74364
|
const populationCall = populationCalls[0];
|
|
75937
74365
|
if (!populationCall) continue;
|
|
75938
74366
|
const populationCallStart = populationCall.range[0];
|
|
75939
|
-
if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))
|
|
74367
|
+
if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))) continue;
|
|
75940
74368
|
if (receiverSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0])) continue;
|
|
75941
74369
|
if (!indexedRelevantCalls(ancestor).some((laterCall) => {
|
|
75942
74370
|
if (laterCall.range[0] <= populationCallStart || laterCall.range[0] >= assertion.range[0] || !isNodeOfType(laterCall.callee, "MemberExpression") || !receiverMatches(laterCall.callee.object)) return false;
|
|
@@ -76046,7 +74474,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
|
|
|
76046
74474
|
const findReceiver = callee.object;
|
|
76047
74475
|
if (predicate && isExhaustiveLiteralTupleMapping(findReceiver, predicate, context)) return;
|
|
76048
74476
|
if (predicate && scopeProvesFindMatch(node, findReceiver, predicate, context)) return;
|
|
76049
|
-
if (predicate && isFindProvenByGuardedMaximum(node, findReceiver, predicate, context)) return;
|
|
76050
74477
|
if (predicate && isEnsureThenFind(node, findReceiver, predicate)) return;
|
|
76051
74478
|
}
|
|
76052
74479
|
if (methodName === "match") {
|
|
@@ -76056,7 +74483,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
|
|
|
76056
74483
|
const regexKey = pattern ? regexComparableKey(pattern, context) : null;
|
|
76057
74484
|
if (pattern && isGuardedAnchoredCharacterMatch(node, matchReceiver, pattern)) return;
|
|
76058
74485
|
if (pattern && regexKey && isMatchProvenByFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
|
|
76059
|
-
if (pattern && regexKey && isMatchProvenByNormalizedFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
|
|
76060
74486
|
if (regexKey && scopeProvesMatchTested(node, regexKey, matchReceiver, context)) return;
|
|
76061
74487
|
}
|
|
76062
74488
|
if (methodName === "get") {
|
|
@@ -79995,23 +78421,16 @@ const MAX_INITIATOR_RESOLUTION_DEPTH = 3;
|
|
|
79995
78421
|
const STATE_DISPATCHER_HOOK_NAMES = new Set(["useState", "useReducer"]);
|
|
79996
78422
|
const REF_HOOK_NAMES = new Set(["useRef"]);
|
|
79997
78423
|
const MESSAGE$26 = "This promise chain runs in an effect, ends in a `.then` that sets state or mutates a ref, and has no `.catch` or enclosing try/catch, so a rejection leaves the state unset and surfaces as an unhandled rejection. Add a `.catch` handler on the chain (`.finally` does not count).";
|
|
79998
|
-
const
|
|
78424
|
+
const isKnownNonThenableHandlerReturn = (expression, context, visitedBindingIdentifiers = /* @__PURE__ */ new Set()) => {
|
|
79999
78425
|
const strippedExpression = stripParenExpression(expression);
|
|
80000
78426
|
if (isDefinitelyNonThenableValue(strippedExpression)) return true;
|
|
80001
|
-
if (isNodeOfType(strippedExpression, "CallExpression") && isNodeOfType(strippedExpression.callee, "MemberExpression")) {
|
|
80002
|
-
const receiver = stripParenExpression(strippedExpression.callee.object);
|
|
80003
|
-
if (isNodeOfType(receiver, "Identifier") && receiver.name === "Promise" && context.scopes.isGlobalReference(receiver) && getStaticPropertyName(strippedExpression.callee) === "resolve") {
|
|
80004
|
-
const resolvedValue = strippedExpression.arguments[0];
|
|
80005
|
-
return !resolvedValue || !isNodeOfType(resolvedValue, "SpreadElement") && isKnownNonRejectingHandlerReturn(resolvedValue, context, visitedBindingIdentifiers);
|
|
80006
|
-
}
|
|
80007
|
-
}
|
|
80008
78427
|
if (!isNodeOfType(strippedExpression, "Identifier")) return false;
|
|
80009
78428
|
if (strippedExpression.name === "undefined" && context.scopes.isGlobalReference(strippedExpression)) return true;
|
|
80010
78429
|
const symbol = context.scopes.symbolFor(strippedExpression);
|
|
80011
78430
|
if (!symbol || visitedBindingIdentifiers.has(symbol.bindingIdentifier)) return false;
|
|
80012
78431
|
visitedBindingIdentifiers.add(symbol.bindingIdentifier);
|
|
80013
78432
|
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
80014
|
-
return Boolean(initializer &&
|
|
78433
|
+
return Boolean(initializer && isKnownNonThenableHandlerReturn(initializer, context, visitedBindingIdentifiers));
|
|
80015
78434
|
};
|
|
80016
78435
|
const isKnownNonRejectingHandler = (argument, context) => {
|
|
80017
78436
|
if (!argument) return false;
|
|
@@ -80026,7 +78445,7 @@ const isKnownNonRejectingHandler = (argument, context) => {
|
|
|
80026
78445
|
canReject = true;
|
|
80027
78446
|
return false;
|
|
80028
78447
|
}
|
|
80029
|
-
if (isNodeOfType(child, "ReturnStatement") && child.argument && !
|
|
78448
|
+
if (isNodeOfType(child, "ReturnStatement") && child.argument && !isKnownNonThenableHandlerReturn(child.argument, context)) {
|
|
80030
78449
|
if (!isNodeOfType(stripParenExpression(child.argument), "CallExpression")) {
|
|
80031
78450
|
canReject = true;
|
|
80032
78451
|
return false;
|
|
@@ -80075,28 +78494,6 @@ const handlerHasPotentiallyThrowingMemberRead = (argument, context) => {
|
|
|
80075
78494
|
});
|
|
80076
78495
|
return hasPotentiallyThrowingMemberRead;
|
|
80077
78496
|
};
|
|
80078
|
-
const hasRejectionHandler = (chain, argument, context, allowTerminalCatchBlock) => {
|
|
80079
|
-
if (!argument) return false;
|
|
80080
|
-
if (!handlerHasPotentiallyThrowingMemberRead(argument, context) && (chainCarriesRejectionHandler(chain, context.scopes) || isKnownNonRejectingHandler(argument, context))) return true;
|
|
80081
|
-
if (!allowTerminalCatchBlock) return false;
|
|
80082
|
-
const candidate = stripParenExpression(argument);
|
|
80083
|
-
const handler = isNodeOfType(candidate, "Identifier") ? resolveExactLocalFunction(candidate, context.scopes) : candidate;
|
|
80084
|
-
if (!handler || !isFunctionLike$1(handler)) return isNodeOfType(candidate, "MemberExpression") || isNodeOfType(candidate, "Identifier") && candidate.name !== "undefined";
|
|
80085
|
-
if (!isNodeOfType(handler.body, "BlockStatement")) return false;
|
|
80086
|
-
let doesExplicitlyReject = false;
|
|
80087
|
-
walkOwnFunctionScope(handler, (child) => {
|
|
80088
|
-
if (doesExplicitlyReject) return false;
|
|
80089
|
-
if (isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "AwaitExpression")) {
|
|
80090
|
-
doesExplicitlyReject = true;
|
|
80091
|
-
return false;
|
|
80092
|
-
}
|
|
80093
|
-
if (isNodeOfType(child, "ReturnStatement") && child.argument && !isKnownNonRejectingHandlerReturn(child.argument, context)) {
|
|
80094
|
-
doesExplicitlyReject = true;
|
|
80095
|
-
return false;
|
|
80096
|
-
}
|
|
80097
|
-
});
|
|
80098
|
-
return !doesExplicitlyReject;
|
|
80099
|
-
};
|
|
80100
78497
|
const walkPromiseChain = (chainExpression, context) => {
|
|
80101
78498
|
let cursor = stripParenExpression(chainExpression);
|
|
80102
78499
|
let hasCatch = false;
|
|
@@ -80108,9 +78505,10 @@ const walkPromiseChain = (chainExpression, context) => {
|
|
|
80108
78505
|
while (isNodeOfType(cursor, "CallExpression") && isNodeOfType(cursor.callee, "MemberExpression") && PROMISE_METHOD_NAMES.has(getStaticPropertyName(cursor.callee) ?? "")) {
|
|
80109
78506
|
const methodName = getStaticPropertyName(cursor.callee);
|
|
80110
78507
|
const rejectionHandlerArgument = methodName === "catch" ? cursor.arguments[0] : cursor.arguments[1];
|
|
80111
|
-
|
|
78508
|
+
const hasAbsorbingRejectionHandler = !handlerHasPotentiallyThrowingMemberRead(rejectionHandlerArgument, context) && (chainCarriesRejectionHandler(cursor, context.scopes) || isKnownNonRejectingHandler(rejectionHandlerArgument, context));
|
|
78509
|
+
if (!didReachTerminalThen && methodName === "catch" && hasAbsorbingRejectionHandler) hasCatch = true;
|
|
80112
78510
|
if (methodName === "then") {
|
|
80113
|
-
if (!didReachTerminalThen &&
|
|
78511
|
+
if (!didReachTerminalThen && hasAbsorbingRejectionHandler) hasRejectionHandlerArgument = true;
|
|
80114
78512
|
didReachTerminalThen = true;
|
|
80115
78513
|
sawThen = true;
|
|
80116
78514
|
const callbackArgument = cursor.arguments[0];
|
|
@@ -82437,45 +80835,6 @@ const noRefCallbackCleanupBeforeReact19 = defineRule({
|
|
|
82437
80835
|
} })
|
|
82438
80836
|
});
|
|
82439
80837
|
//#endregion
|
|
82440
|
-
//#region src/plugin/utils/contains-non-deterministic-source.ts
|
|
82441
|
-
const NON_DETERMINISTIC_MEMBER_CALLS = new Set([
|
|
82442
|
-
"Math.random",
|
|
82443
|
-
"Date.now",
|
|
82444
|
-
"performance.now",
|
|
82445
|
-
"crypto.randomUUID",
|
|
82446
|
-
"crypto.getRandomValues"
|
|
82447
|
-
]);
|
|
82448
|
-
const NON_DETERMINISTIC_ID_GENERATOR_NAMES = new Set([
|
|
82449
|
-
"nanoid",
|
|
82450
|
-
"uuid",
|
|
82451
|
-
"cuid",
|
|
82452
|
-
"ulid",
|
|
82453
|
-
"createId"
|
|
82454
|
-
]);
|
|
82455
|
-
const isZeroArgDateConstruction = (node) => isNodeOfType(node, "NewExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Date" && (node.arguments?.length ?? 0) === 0;
|
|
82456
|
-
const containsNonDeterministicSource = (root) => {
|
|
82457
|
-
let found = false;
|
|
82458
|
-
walkAst(root, (child) => {
|
|
82459
|
-
if (found) return false;
|
|
82460
|
-
if (isFunctionLike$1(child)) return false;
|
|
82461
|
-
if (isZeroArgDateConstruction(child)) {
|
|
82462
|
-
found = true;
|
|
82463
|
-
return false;
|
|
82464
|
-
}
|
|
82465
|
-
if (!isNodeOfType(child, "CallExpression")) return;
|
|
82466
|
-
const callee = child.callee;
|
|
82467
|
-
if (isNodeOfType(callee, "Identifier") && NON_DETERMINISTIC_ID_GENERATOR_NAMES.has(callee.name)) {
|
|
82468
|
-
found = true;
|
|
82469
|
-
return false;
|
|
82470
|
-
}
|
|
82471
|
-
if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.object, "Identifier") && isNodeOfType(callee.property, "Identifier") && NON_DETERMINISTIC_MEMBER_CALLS.has(`${callee.object.name}.${callee.property.name}`)) {
|
|
82472
|
-
found = true;
|
|
82473
|
-
return false;
|
|
82474
|
-
}
|
|
82475
|
-
});
|
|
82476
|
-
return found;
|
|
82477
|
-
};
|
|
82478
|
-
//#endregion
|
|
82479
80838
|
//#region src/plugin/rules/state-and-effects/no-ref-current-in-render.ts
|
|
82480
80839
|
const REPEATED_ANCESTOR_TYPES = new Set([
|
|
82481
80840
|
"DoWhileStatement",
|
|
@@ -82506,75 +80865,45 @@ const resolveImmutableInitializationValue = (node, scopes, visitedSymbolIds = /*
|
|
|
82506
80865
|
};
|
|
82507
80866
|
const isProvablyTruthyInitializationValue = (node, scopes) => {
|
|
82508
80867
|
const expression = resolveImmutableInitializationValue(node, scopes);
|
|
82509
|
-
|
|
82510
|
-
if (isNodeOfType(expression, "CallExpression")) {
|
|
82511
|
-
const callee = stripParenExpression(expression.callee);
|
|
82512
|
-
return isNodeOfType(callee, "Identifier") && callee.name.startsWith("create");
|
|
82513
|
-
}
|
|
82514
|
-
return isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression");
|
|
80868
|
+
return Boolean(expression && (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression")));
|
|
82515
80869
|
};
|
|
82516
|
-
const
|
|
80870
|
+
const getInitializationConstructorName = (node, scopes) => {
|
|
82517
80871
|
const expression = resolveImmutableInitializationValue(node, scopes);
|
|
82518
80872
|
if (!expression) return null;
|
|
82519
|
-
if (isNodeOfType(expression, "NewExpression")
|
|
80873
|
+
if (isNodeOfType(expression, "NewExpression")) {
|
|
82520
80874
|
const callee = stripParenExpression(expression.callee);
|
|
82521
|
-
|
|
82522
|
-
return callee.name.startsWith("create") && callee.name.length > 6 ? callee.name.slice(6) : callee.name;
|
|
80875
|
+
return isNodeOfType(callee, "Identifier") ? callee.name : null;
|
|
82523
80876
|
}
|
|
82524
80877
|
return null;
|
|
82525
80878
|
};
|
|
82526
|
-
const isMatchingReturnType = (typeNode, initializationValue, scopes) => {
|
|
82527
|
-
if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
|
|
82528
|
-
const typeName = typeNode.typeName;
|
|
82529
|
-
if (!isNodeOfType(typeName, "Identifier") || typeName.name !== "ReturnType") return false;
|
|
82530
|
-
const [returnTypeArgument] = typeNode.typeArguments?.params ?? [];
|
|
82531
|
-
if (!returnTypeArgument || !isNodeOfType(returnTypeArgument, "TSTypeQuery")) return false;
|
|
82532
|
-
const queriedName = returnTypeArgument.exprName;
|
|
82533
|
-
const expression = stripParenExpression(initializationValue);
|
|
82534
|
-
if (!isNodeOfType(queriedName, "Identifier") || !isNodeOfType(expression, "CallExpression")) return false;
|
|
82535
|
-
const callee = stripParenExpression(expression.callee);
|
|
82536
|
-
if (!isNodeOfType(callee, "Identifier")) return false;
|
|
82537
|
-
const queriedSymbol = scopes.symbolFor(queriedName);
|
|
82538
|
-
const calleeSymbol = scopes.symbolFor(callee);
|
|
82539
|
-
return queriedSymbol && calleeSymbol ? queriedSymbol.id === calleeSymbol.id : queriedName.name === callee.name;
|
|
82540
|
-
};
|
|
82541
80879
|
const isClosedTruthyTypeDomain = (typeNode, initializationValue, scopes) => {
|
|
82542
80880
|
const initializationExpression = stripParenExpression(initializationValue);
|
|
82543
80881
|
if (isNodeOfType(typeNode, "TSTypeLiteral")) return isNodeOfType(initializationExpression, "ObjectExpression");
|
|
82544
80882
|
if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return isNodeOfType(initializationExpression, "ArrayExpression");
|
|
82545
80883
|
if (isNodeOfType(typeNode, "TSFunctionType") || isNodeOfType(typeNode, "TSConstructorType")) return isNodeOfType(initializationExpression, "ArrowFunctionExpression") || isNodeOfType(initializationExpression, "FunctionExpression") || isNodeOfType(initializationExpression, "ClassExpression");
|
|
82546
80884
|
if (isNodeOfType(typeNode, "TSObjectKeyword")) return true;
|
|
82547
|
-
if (isNodeOfType(typeNode, "TSIndexedAccessType")) return isNodeOfType(initializationExpression, "ObjectExpression");
|
|
82548
80885
|
if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
|
|
82549
80886
|
const typeName = typeNode.typeName;
|
|
82550
|
-
|
|
82551
|
-
return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationValueName(initializationExpression, scopes);
|
|
80887
|
+
return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationConstructorName(initializationExpression, scopes);
|
|
82552
80888
|
};
|
|
82553
80889
|
const refHasClosedFalsySentinelDomain = (refSymbol, initializationValue, scopes) => {
|
|
82554
80890
|
const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
|
|
82555
80891
|
if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
82556
80892
|
const [initialValue] = initializer.arguments ?? [];
|
|
82557
|
-
if (initialValue
|
|
80893
|
+
if (!initialValue || isNodeOfType(initialValue, "SpreadElement") || !isEmptySentinel(initialValue, scopes)) return false;
|
|
82558
80894
|
const [declaredType] = initializer.typeArguments?.params ?? [];
|
|
82559
|
-
if (!declaredType) return false;
|
|
82560
|
-
|
|
80895
|
+
if (!declaredType || !isNodeOfType(declaredType, "TSUnionType")) return false;
|
|
80896
|
+
let hasEmptySentinel = false;
|
|
82561
80897
|
let hasTruthyDomain = false;
|
|
82562
|
-
for (const memberType of
|
|
82563
|
-
if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword"))
|
|
80898
|
+
for (const memberType of declaredType.types ?? []) {
|
|
80899
|
+
if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) {
|
|
80900
|
+
hasEmptySentinel = true;
|
|
80901
|
+
continue;
|
|
80902
|
+
}
|
|
82564
80903
|
if (!isClosedTruthyTypeDomain(memberType, initializationValue, scopes)) return false;
|
|
82565
80904
|
hasTruthyDomain = true;
|
|
82566
80905
|
}
|
|
82567
|
-
return hasTruthyDomain;
|
|
82568
|
-
};
|
|
82569
|
-
const refHasEmptySentinelInitializer = (refSymbol, scopes) => {
|
|
82570
|
-
const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
|
|
82571
|
-
if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
82572
|
-
const [initialValue] = initializer.arguments ?? [];
|
|
82573
|
-
return Boolean(!initialValue || !isNodeOfType(initialValue, "SpreadElement") && isEmptySentinel(initialValue, scopes));
|
|
82574
|
-
};
|
|
82575
|
-
const refHasDeclaredType = (refSymbol) => {
|
|
82576
|
-
const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
|
|
82577
|
-
return Boolean(initializer && isNodeOfType(initializer, "CallExpression") && (initializer.typeArguments?.params.length ?? 0) > 0);
|
|
80906
|
+
return hasEmptySentinel && hasTruthyDomain;
|
|
82578
80907
|
};
|
|
82579
80908
|
const isSafeRefIdentifierUse = (identifier) => {
|
|
82580
80909
|
const expressionRoot = findTransparentExpressionRoot(identifier);
|
|
@@ -82606,40 +80935,25 @@ const expressionContainsRefCurrent = (expression, refSymbol, scopes) => {
|
|
|
82606
80935
|
});
|
|
82607
80936
|
return didFindRefCurrent;
|
|
82608
80937
|
};
|
|
82609
|
-
const
|
|
82610
|
-
|
|
82611
|
-
|
|
82612
|
-
|
|
82613
|
-
|
|
82614
|
-
|
|
82615
|
-
|
|
82616
|
-
if (!isInputIndependent) return false;
|
|
82617
|
-
if (resolveReactRefSymbol(child, scopes)) return false;
|
|
82618
|
-
if (!isNodeOfType(child, "Identifier")) return;
|
|
82619
|
-
const symbol = scopes.symbolFor(child);
|
|
82620
|
-
if (!symbol) return;
|
|
82621
|
-
if (symbol.kind === "import") return false;
|
|
82622
|
-
if (symbol.kind === "let" || symbol.kind === "var" || symbol.kind === "using") {
|
|
82623
|
-
isInputIndependent = false;
|
|
82624
|
-
return false;
|
|
80938
|
+
const hasNoCompetingRefCurrentWrite = (branchRoot, assignmentExpression, refSymbol, scopes) => {
|
|
80939
|
+
let writeCount = 0;
|
|
80940
|
+
walkAst(branchRoot, (child) => {
|
|
80941
|
+
if (writeCount > 1) return false;
|
|
80942
|
+
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
80943
|
+
if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
|
|
80944
|
+
return;
|
|
82625
80945
|
}
|
|
82626
|
-
if (
|
|
82627
|
-
|
|
82628
|
-
|
|
82629
|
-
return false;
|
|
80946
|
+
if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
|
|
80947
|
+
if (expressionContainsRefCurrent(child.argument, refSymbol, scopes)) writeCount++;
|
|
80948
|
+
return;
|
|
82630
80949
|
}
|
|
82631
|
-
if (
|
|
82632
|
-
|
|
82633
|
-
return false;
|
|
80950
|
+
if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) {
|
|
80951
|
+
if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
|
|
82634
80952
|
}
|
|
82635
|
-
if (visitedSymbolIds.has(symbol.id)) return false;
|
|
82636
|
-
visitedSymbolIds.add(symbol.id);
|
|
82637
|
-
if (!isInitializationInputIndependent(symbol.initializer, renderOwner, scopes, visitedSymbolIds)) isInputIndependent = false;
|
|
82638
|
-
return false;
|
|
82639
80953
|
});
|
|
82640
|
-
return
|
|
80954
|
+
return writeCount === 1 && expressionContainsRefCurrent(assignmentExpression.left, refSymbol, scopes);
|
|
82641
80955
|
};
|
|
82642
|
-
const
|
|
80956
|
+
const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
|
|
82643
80957
|
const hasRepeatedExecutionAncestor = (node, stop) => {
|
|
82644
80958
|
let ancestor = node.parent;
|
|
82645
80959
|
while (ancestor && ancestor !== stop) {
|
|
@@ -82669,27 +80983,6 @@ const canExecuteTogether = (firstConstraints, secondConstraints) => {
|
|
|
82669
80983
|
}
|
|
82670
80984
|
return true;
|
|
82671
80985
|
};
|
|
82672
|
-
const hasNoCoExecutableCompetingWrite = (assignmentExpression, renderOwner, refSymbol, scopes) => {
|
|
82673
|
-
const assignmentConstraints = getBranchConstraints(assignmentExpression, renderOwner);
|
|
82674
|
-
const synchronouslyInvokedFunctions = collectSynchronouslyEffectInvokedFunctions(renderOwner, scopes);
|
|
82675
|
-
let hasCompetingWrite = false;
|
|
82676
|
-
walkAst(renderOwner, (child) => {
|
|
82677
|
-
if (hasCompetingWrite) return false;
|
|
82678
|
-
let writtenExpression = null;
|
|
82679
|
-
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
82680
|
-
if (child === assignmentExpression) return;
|
|
82681
|
-
writtenExpression = child.left;
|
|
82682
|
-
} else if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") writtenExpression = child.argument;
|
|
82683
|
-
else if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) writtenExpression = child.left;
|
|
82684
|
-
const deferredExecutionBoundary = findDeferredExecutionBoundary(child);
|
|
82685
|
-
const deferredWriteValue = isNodeOfType(child, "AssignmentExpression") && child.operator === "=" ? resolveImmutableInitializationValue(child.right, scopes) : null;
|
|
82686
|
-
const isDeferredTruthyWrite = deferredExecutionBoundary !== null && deferredExecutionBoundary !== renderOwner && !synchronouslyInvokedFunctions.has(deferredExecutionBoundary) && !executesDuringRender(deferredExecutionBoundary, scopes) && deferredWriteValue !== null && !isNodeOfType(deferredWriteValue, "CallExpression") && isProvablyTruthyInitializationValue(deferredWriteValue, scopes);
|
|
82687
|
-
if (!writtenExpression || isDeferredTruthyWrite || !expressionContainsRefCurrent(writtenExpression, refSymbol, scopes) || !canExecuteTogether(assignmentConstraints, getBranchConstraints(child, renderOwner))) return;
|
|
82688
|
-
hasCompetingWrite = true;
|
|
82689
|
-
return false;
|
|
82690
|
-
});
|
|
82691
|
-
return !hasCompetingWrite;
|
|
82692
|
-
};
|
|
82693
80986
|
const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol, scopes) => {
|
|
82694
80987
|
const assignmentConstraints = getBranchConstraints(assignmentExpression, branchRoot);
|
|
82695
80988
|
const assignmentStart = getRangeStart(assignmentExpression);
|
|
@@ -82705,17 +80998,16 @@ const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol
|
|
|
82705
80998
|
});
|
|
82706
80999
|
return !hasCoExecutableWrite;
|
|
82707
81000
|
};
|
|
82708
|
-
const isPredictableGuardedInitialization = (assignmentExpression, guardedBranch, renderOwner, refSymbol, scopes, requiresClosedTruthyDomain) => refHasEmptySentinelInitializer(refSymbol, scopes) && isPredictableInitializationValue(assignmentExpression.right, refSymbol, renderOwner, scopes, requiresClosedTruthyDomain) && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && (guardedBranch === renderOwner || !hasRepeatedExecutionAncestor(guardedBranch, renderOwner)) && hasNoPriorCoExecutableWrite(assignmentExpression, renderOwner, refSymbol, scopes) && hasNoCoExecutableCompetingWrite(assignmentExpression, renderOwner, refSymbol, scopes) && refDoesNotEscape(renderOwner, refSymbol, scopes);
|
|
82709
81001
|
const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
|
|
81002
|
+
if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
|
|
81003
|
+
if (assignmentExpression.operator !== "=") return false;
|
|
82710
81004
|
const renderOwner = findRenderPhaseComponentOrHook(assignmentExpression, scopes);
|
|
82711
81005
|
if (!renderOwner) return false;
|
|
82712
|
-
if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return isPredictableGuardedInitialization(assignmentExpression, renderOwner, renderOwner, refSymbol, scopes, assignmentExpression.operator === "||=");
|
|
82713
|
-
if (assignmentExpression.operator !== "=") return false;
|
|
82714
81006
|
let descendant = assignmentExpression;
|
|
82715
81007
|
let ancestor = descendant.parent;
|
|
82716
81008
|
while (ancestor) {
|
|
82717
81009
|
const test = isNodeOfType(ancestor, "IfStatement") ? stripParenExpression(ancestor.test) : null;
|
|
82718
|
-
if (isNodeOfType(ancestor, "IfStatement") && test && isNodeOfType(test, "UnaryExpression") && test.operator === "!" && isSameRefCurrentAlias(test.argument, refSymbol, scopes) && ancestor.consequent === descendant &&
|
|
81010
|
+
if (isNodeOfType(ancestor, "IfStatement") && test && isNodeOfType(test, "UnaryExpression") && test.operator === "!" && isSameRefCurrentAlias(test.argument, refSymbol, scopes) && ancestor.consequent === descendant && isProvablyTruthyInitializationValue(assignmentExpression.right, scopes) && refHasClosedFalsySentinelDomain(refSymbol, assignmentExpression.right, scopes) && !hasRepeatedExecutionAncestor(assignmentExpression, ancestor.consequent) && !hasRepeatedExecutionAncestor(ancestor, renderOwner) && hasNoPriorCoExecutableWrite(assignmentExpression, ancestor.consequent, refSymbol, scopes) && hasNoCompetingRefCurrentWrite(renderOwner, assignmentExpression, refSymbol, scopes) && refDoesNotEscape(renderOwner, refSymbol, scopes)) return true;
|
|
82719
81011
|
if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(test, "BinaryExpression") && [
|
|
82720
81012
|
"===",
|
|
82721
81013
|
"==",
|
|
@@ -82725,7 +81017,7 @@ const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes)
|
|
|
82725
81017
|
const { left, right } = test;
|
|
82726
81018
|
const comparesEmptySentinel = isSameRefCurrentAlias(left, refSymbol, scopes) && isEmptySentinel(right, scopes) || isSameRefCurrentAlias(right, refSymbol, scopes) && isEmptySentinel(left, scopes);
|
|
82727
81019
|
const guardedBranch = test.operator === "===" || test.operator === "==" ? ancestor.consequent : ancestor.alternate;
|
|
82728
|
-
if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch &&
|
|
81020
|
+
if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
|
|
82729
81021
|
}
|
|
82730
81022
|
descendant = ancestor;
|
|
82731
81023
|
ancestor = descendant.parent;
|
|
@@ -110532,6 +108824,9 @@ const rerenderFunctionalSetstate = defineRule({
|
|
|
110532
108824
|
} })
|
|
110533
108825
|
});
|
|
110534
108826
|
//#endregion
|
|
108827
|
+
//#region src/plugin/utils/is-trivial-built-in-construction.ts
|
|
108828
|
+
const isTrivialBuiltInConstruction = (expression) => isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && TRIVIAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && (expression.arguments ?? []).length === 0;
|
|
108829
|
+
//#endregion
|
|
110535
108830
|
//#region src/plugin/rules/state-and-effects/rerender-lazy-ref-init.ts
|
|
110536
108831
|
const rerenderLazyRefInit = defineRule({
|
|
110537
108832
|
id: "rerender-lazy-ref-init",
|
|
@@ -110550,6 +108845,7 @@ const rerenderLazyRefInit = defineRule({
|
|
|
110550
108845
|
const memberPropertyName = isNodeOfType(callee, "MemberExpression") && (isNodeOfType(callee.property, "Identifier") || isNodeOfType(callee.property, "PrivateIdentifier")) ? callee.property.name : null;
|
|
110551
108846
|
const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : memberPropertyName ?? "fn";
|
|
110552
108847
|
if (TRIVIAL_INITIALIZER_NAMES.has(calleeName)) return;
|
|
108848
|
+
if (isTrivialBuiltInConstruction(initializer)) return;
|
|
110553
108849
|
if (isPlainCall && isReactHookName(calleeName)) return;
|
|
110554
108850
|
const callShape = isNewCall ? `new ${calleeName}()` : `${calleeName}()`;
|
|
110555
108851
|
context.report({
|