oxlint-plugin-react-doctor 0.9.2-dev.9512488 → 0.9.2-dev.b10cd4c
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 +135 -1512
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -388,7 +388,7 @@ const SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?)$/i;
|
|
|
388
388
|
const SCRIPT_SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?|py|php)$/i;
|
|
389
389
|
const DATABASE_SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?|py)$/i;
|
|
390
390
|
const SERVER_CONTEXT_PATTERN = /(?:^|\/)(?:api|backend|server|servers|middleware|route|routes|functions|lambdas|workers)(?:\/|$)|(?:^|\/)[^/]+\.server\.[cm]?[jt]sx?$/i;
|
|
391
|
-
const TEST_CONTEXT_PATTERN = /(?:^|\/)(?:__fixtures__|__mocks__|__tests__|__integration__|fixtures|mocks|test|tests|testdata|test-data|e2e|playwright|cypress|specs?)(?:\/|$)
|
|
391
|
+
const TEST_CONTEXT_PATTERN = /(?:^|\/)(?:__fixtures__|__mocks__|__tests__|__integration__|fixtures|mocks|test|tests|testdata|test-data|e2e|playwright|cypress|specs?)(?:\/|$)|\.(?:test|spec|e2e|e2e-spec|integration-test|fixture|fixtures|stories|story)\.[cm]?[jt]sx?$|(?:^|\/)(?:playwright|cypress|vitest|jest|karma)[^/]*\.conf(?:ig)?\.[cm]?[jt]s$|(?:^|\/)(?:test_[^/]+|[^/]+_test|conftest)\.py$|\.env\.[^/]*(?:test|e2e)[^/]*$/i;
|
|
392
392
|
const BUILD_CONFIG_FILE_PATTERN = /(?:^|\/)(?:vite|next|nuxt|astro|remix|webpack|rollup|rspack|rsbuild|esbuild|tsup|metro|expo|babel|tailwind|postcss|svelte|farm|parcel|snowpack)[^/]*\.config\.[cm]?[jt]sx?$/i;
|
|
393
393
|
const BUILD_SCRIPT_CONTEXT_PATTERN = /(?:^|\/)scripts(?:\/|$)/i;
|
|
394
394
|
const DEMO_CONTEXT_PATTERN = /(?:^|\/)(?:examples?|tutorials?|demos?|samples?|playgrounds?)(?:\/|$)/i;
|
|
@@ -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
|
}
|
|
@@ -30897,8 +30534,7 @@ const STRING_TYPED_PROPERTY_NAMES = new Set([
|
|
|
30897
30534
|
"code",
|
|
30898
30535
|
"label",
|
|
30899
30536
|
"slug",
|
|
30900
|
-
"prefix"
|
|
30901
|
-
"__html"
|
|
30537
|
+
"prefix"
|
|
30902
30538
|
]);
|
|
30903
30539
|
const STRING_TYPED_IDENTIFIER_SUFFIXES = [
|
|
30904
30540
|
"Text",
|
|
@@ -31016,25 +30652,13 @@ const STRING_TYPED_IDENTIFIER_NAMES = new Set([
|
|
|
31016
30652
|
"title"
|
|
31017
30653
|
]);
|
|
31018
30654
|
const STRING_RETURNING_CALLEE_PREFIX_PATTERN = /^(?:normalize|format|stringify|serialize)/;
|
|
31019
|
-
const FRESH_ARRAY_METHOD_NAMES$2 = new Set([
|
|
31020
|
-
"concat",
|
|
31021
|
-
"filter",
|
|
31022
|
-
"flat",
|
|
31023
|
-
"flatMap",
|
|
31024
|
-
"map",
|
|
31025
|
-
"slice",
|
|
31026
|
-
"split"
|
|
31027
|
-
]);
|
|
31028
30655
|
const isLikelyStringReceiver = (receiver) => {
|
|
31029
30656
|
if (!receiver) return false;
|
|
31030
|
-
const unwrappedReceiver = stripParenExpression(receiver);
|
|
31031
|
-
if (unwrappedReceiver !== receiver) return isLikelyStringReceiver(unwrappedReceiver);
|
|
31032
30657
|
if (isNodeOfType(receiver, "Literal") && typeof receiver.value === "string") return true;
|
|
31033
30658
|
if (isNodeOfType(receiver, "TemplateLiteral")) return true;
|
|
31034
30659
|
if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && receiver.callee.name === "String") return true;
|
|
31035
30660
|
if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && STRING_RETURNING_METHODS.has(receiver.callee.property.name)) return true;
|
|
31036
30661
|
if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && STRING_RETURNING_CALLEE_PREFIX_PATTERN.test(receiver.callee.name)) return true;
|
|
31037
|
-
if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && (receiver.callee.property.name === "concat" || receiver.callee.property.name === "slice") && isLikelyStringReceiver(receiver.callee.object)) return true;
|
|
31038
30662
|
if (isNodeOfType(receiver, "MemberExpression") && isNodeOfType(receiver.property, "Identifier")) {
|
|
31039
30663
|
if (STRING_TYPED_PROPERTY_NAMES.has(receiver.property.name)) return true;
|
|
31040
30664
|
}
|
|
@@ -31052,46 +30676,8 @@ const isLikelyStringReceiver = (receiver) => {
|
|
|
31052
30676
|
}
|
|
31053
30677
|
if (isNodeOfType(receiver, "BinaryExpression") && receiver.operator === "+") return isLikelyStringReceiver(receiver.left) || isLikelyStringReceiver(receiver.right);
|
|
31054
30678
|
if (isNodeOfType(receiver, "ConditionalExpression")) return isLikelyStringReceiver(receiver.consequent) && isLikelyStringReceiver(receiver.alternate);
|
|
31055
|
-
if (isNodeOfType(receiver, "LogicalExpression")) return isLikelyStringReceiver(receiver.left) && isLikelyStringReceiver(receiver.right);
|
|
31056
30679
|
return false;
|
|
31057
30680
|
};
|
|
31058
|
-
const isFreshArrayReceiver = (receiver) => {
|
|
31059
|
-
const unwrappedReceiver = stripParenExpression(receiver);
|
|
31060
|
-
if (unwrappedReceiver !== receiver) return isFreshArrayReceiver(unwrappedReceiver);
|
|
31061
|
-
if (!isNodeOfType(receiver, "CallExpression") || !isNodeOfType(receiver.callee, "MemberExpression") || !isNodeOfType(receiver.callee.property, "Identifier")) return false;
|
|
31062
|
-
if (!FRESH_ARRAY_METHOD_NAMES$2.has(receiver.callee.property.name)) return false;
|
|
31063
|
-
if (receiver.callee.property.name === "split") return isLikelyStringReceiver(receiver.callee.object);
|
|
31064
|
-
const sourceReceiver = stripParenExpression(receiver.callee.object);
|
|
31065
|
-
return isKnownNativeArrayReceiver(sourceReceiver) || isFreshArrayReceiver(sourceReceiver);
|
|
31066
|
-
};
|
|
31067
|
-
const isSmallRestHelperOmissionList = (node) => {
|
|
31068
|
-
if (!node) return false;
|
|
31069
|
-
const candidate = stripParenExpression(node);
|
|
31070
|
-
if (!isNodeOfType(candidate, "ArrayExpression")) return false;
|
|
31071
|
-
const elements = candidate.elements ?? [];
|
|
31072
|
-
return elements.length <= 8 && elements.every((element) => element === null || !isNodeOfType(element, "SpreadElement"));
|
|
31073
|
-
};
|
|
31074
|
-
const isTypeScriptRestHelperLookup = (lookupCall, receiver, scopes) => {
|
|
31075
|
-
if (!isNodeOfType(receiver, "Identifier")) return false;
|
|
31076
|
-
const enclosingFunction = findEnclosingFunction$1(lookupCall);
|
|
31077
|
-
if (!isNodeOfType(enclosingFunction, "FunctionExpression") || !isNodeOfType(enclosingFunction.params?.[1], "Identifier") || enclosingFunction.params[1].name !== receiver.name) return false;
|
|
31078
|
-
let bindingIdentifier = null;
|
|
31079
|
-
let ancestor = enclosingFunction.parent;
|
|
31080
|
-
while (ancestor && !isFunctionLike$1(ancestor)) {
|
|
31081
|
-
if (isNodeOfType(ancestor, "VariableDeclarator") && isNodeOfType(ancestor.id, "Identifier") && ancestor.id.name === "__rest") {
|
|
31082
|
-
bindingIdentifier = ancestor.id;
|
|
31083
|
-
break;
|
|
31084
|
-
}
|
|
31085
|
-
ancestor = ancestor.parent;
|
|
31086
|
-
}
|
|
31087
|
-
if (!bindingIdentifier) return false;
|
|
31088
|
-
const helperSymbol = scopes.symbolFor(bindingIdentifier);
|
|
31089
|
-
if (!helperSymbol || helperSymbol.references.length === 0) return false;
|
|
31090
|
-
return helperSymbol.references.every((reference) => {
|
|
31091
|
-
const callExpression = reference.identifier.parent;
|
|
31092
|
-
return isNodeOfType(callExpression, "CallExpression") && callExpression.callee === reference.identifier && isSmallRestHelperOmissionList(callExpression.arguments?.[1]);
|
|
31093
|
-
});
|
|
31094
|
-
};
|
|
31095
30681
|
const INDEX_LIKE_IDENTIFIER_NAMES = new Set([
|
|
31096
30682
|
"i",
|
|
31097
30683
|
"j",
|
|
@@ -31688,8 +31274,6 @@ const jsSetMapLookups = defineRule({
|
|
|
31688
31274
|
const query = node.arguments[0];
|
|
31689
31275
|
if (methodName === "indexOf" && !isKnownSafeIndexOfQuery(query) && (isKnownUnsafeIndexOfQuery(query, receiver) || isKnownUnsafeIndexOfReceiver(receiver))) return;
|
|
31690
31276
|
if (isLikelyStringReceiver(receiver)) return;
|
|
31691
|
-
if (isFreshArrayReceiver(receiver)) return;
|
|
31692
|
-
if (isTypeScriptRestHelperLookup(node, receiver, context.scopes)) return;
|
|
31693
31277
|
if (isSmallInlineLiteralArray(receiver)) return;
|
|
31694
31278
|
if (isScreamingSnakeCaseConstantReceiver(receiver)) return;
|
|
31695
31279
|
if (isSmallFixedListMember(receiver)) return;
|
|
@@ -45603,6 +45187,14 @@ const noAriaInvalidWithoutDescription = defineRule({
|
|
|
45603
45187
|
} })
|
|
45604
45188
|
});
|
|
45605
45189
|
//#endregion
|
|
45190
|
+
//#region src/plugin/utils/is-early-exit-statement.ts
|
|
45191
|
+
const isEarlyExitStatement$1 = (statement) => {
|
|
45192
|
+
if (!statement) return false;
|
|
45193
|
+
if (statementAlwaysExits$1(statement)) return true;
|
|
45194
|
+
if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
|
|
45195
|
+
return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
|
|
45196
|
+
};
|
|
45197
|
+
//#endregion
|
|
45606
45198
|
//#region src/plugin/utils/unwrap-negative-guard-form.ts
|
|
45607
45199
|
const unwrapNegativeGuardForm = (test) => {
|
|
45608
45200
|
const expression = stripParenExpression(test);
|
|
@@ -47123,101 +46715,6 @@ const isLoopCounterDeclarator = (declarator, referenceNode, indexName) => {
|
|
|
47123
46715
|
}
|
|
47124
46716
|
return isBindingReassignedOrMutated(referenceNode, indexName);
|
|
47125
46717
|
};
|
|
47126
|
-
const findEnclosingWhileLoop = (node) => {
|
|
47127
|
-
let current = node.parent;
|
|
47128
|
-
while (current) {
|
|
47129
|
-
if (isNodeOfType(current, "WhileStatement") || isNodeOfType(current, "DoWhileStatement")) return current;
|
|
47130
|
-
if (isFunctionLike$1(current) || isNodeOfType(current, "Program")) return null;
|
|
47131
|
-
current = current.parent;
|
|
47132
|
-
}
|
|
47133
|
-
return null;
|
|
47134
|
-
};
|
|
47135
|
-
const isStaticMemberChain = (expression) => {
|
|
47136
|
-
const candidate = stripParenExpression(expression);
|
|
47137
|
-
if (isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "ThisExpression")) return true;
|
|
47138
|
-
return Boolean(isNodeOfType(candidate, "MemberExpression") && !candidate.computed && isNodeOfType(candidate.property, "Identifier") && isStaticMemberChain(candidate.object));
|
|
47139
|
-
};
|
|
47140
|
-
const findLengthBoundCollections = (expression) => {
|
|
47141
|
-
const collections = [];
|
|
47142
|
-
walkAst(expression, (child) => {
|
|
47143
|
-
if (isNodeOfType(child, "MemberExpression") && !child.computed && isStaticMemberChain(child.object) && isNodeOfType(child.property, "Identifier") && child.property.name === "length") collections.push(child.object);
|
|
47144
|
-
});
|
|
47145
|
-
return collections;
|
|
47146
|
-
};
|
|
47147
|
-
const areSameBoundStaticMemberChains = (first, second) => isStaticMemberChain(first) && isStaticMemberChain(second) && areExpressionsStructurallyEqual(first, second, { areIdentifiersEqual: (firstIdentifier, secondIdentifier) => {
|
|
47148
|
-
if (!isNodeOfType(firstIdentifier, "Identifier") || !isNodeOfType(secondIdentifier, "Identifier") || firstIdentifier.name !== secondIdentifier.name) return false;
|
|
47149
|
-
const firstBinding = findVariableInitializer(firstIdentifier, firstIdentifier.name);
|
|
47150
|
-
const secondBinding = findVariableInitializer(secondIdentifier, secondIdentifier.name);
|
|
47151
|
-
return firstBinding?.bindingIdentifier === secondBinding?.bindingIdentifier;
|
|
47152
|
-
} });
|
|
47153
|
-
const RELATIONAL_BOUND_OPERATORS = new Set([
|
|
47154
|
-
"<",
|
|
47155
|
-
"<=",
|
|
47156
|
-
">",
|
|
47157
|
-
">="
|
|
47158
|
-
]);
|
|
47159
|
-
const loopTestBoundsCounterByLength = (expression, indexName, bindingIdentifier) => {
|
|
47160
|
-
const readsCounter = (candidate) => {
|
|
47161
|
-
let didReadCounter = false;
|
|
47162
|
-
walkAst(candidate, (child) => {
|
|
47163
|
-
if (didReadCounter) return false;
|
|
47164
|
-
if (isNodeOfType(child, "Identifier") && child.name === indexName && findVariableInitializer(child, indexName)?.bindingIdentifier === bindingIdentifier) {
|
|
47165
|
-
didReadCounter = true;
|
|
47166
|
-
return false;
|
|
47167
|
-
}
|
|
47168
|
-
});
|
|
47169
|
-
return didReadCounter;
|
|
47170
|
-
};
|
|
47171
|
-
const readsLength = (candidate) => {
|
|
47172
|
-
let didReadLength = false;
|
|
47173
|
-
walkAst(candidate, (child) => {
|
|
47174
|
-
if (didReadLength) return false;
|
|
47175
|
-
if (isNodeOfType(child, "MemberExpression") && !child.computed && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
|
|
47176
|
-
didReadLength = true;
|
|
47177
|
-
return false;
|
|
47178
|
-
}
|
|
47179
|
-
});
|
|
47180
|
-
return didReadLength;
|
|
47181
|
-
};
|
|
47182
|
-
let didFindLengthBound = false;
|
|
47183
|
-
walkAst(expression, (child) => {
|
|
47184
|
-
if (didFindLengthBound) return false;
|
|
47185
|
-
if (!isNodeOfType(child, "BinaryExpression") || !RELATIONAL_BOUND_OPERATORS.has(child.operator)) return;
|
|
47186
|
-
if (readsCounter(child.left) && readsLength(child.right) || readsCounter(child.right) && readsLength(child.left)) {
|
|
47187
|
-
didFindLengthBound = true;
|
|
47188
|
-
return false;
|
|
47189
|
-
}
|
|
47190
|
-
});
|
|
47191
|
-
return didFindLengthBound;
|
|
47192
|
-
};
|
|
47193
|
-
const isDataIndexedWhileLoopCounter = (referenceNode, bindingIdentifier, indexName) => {
|
|
47194
|
-
if (!INDEX_PARAMETER_NAMES.has(indexName)) return false;
|
|
47195
|
-
const loop = findEnclosingWhileLoop(referenceNode);
|
|
47196
|
-
if (!loop) return false;
|
|
47197
|
-
let doesTestReadCounter = false;
|
|
47198
|
-
walkAst(loop.test, (child) => {
|
|
47199
|
-
if (doesTestReadCounter) return false;
|
|
47200
|
-
if (!isNodeOfType(child, "Identifier") || child.name !== indexName) return;
|
|
47201
|
-
if (findVariableInitializer(child, indexName)?.bindingIdentifier === bindingIdentifier) {
|
|
47202
|
-
doesTestReadCounter = true;
|
|
47203
|
-
return false;
|
|
47204
|
-
}
|
|
47205
|
-
});
|
|
47206
|
-
if (!doesTestReadCounter) return false;
|
|
47207
|
-
const lengthBoundCollections = findLengthBoundCollections(loop.test);
|
|
47208
|
-
if (lengthBoundCollections.length === 0) return false;
|
|
47209
|
-
let didFindIndexedCollectionRead = false;
|
|
47210
|
-
walkAst(loop.body, (child) => {
|
|
47211
|
-
if (didFindIndexedCollectionRead) return false;
|
|
47212
|
-
if (isFunctionLike$1(child)) return false;
|
|
47213
|
-
if (!isNodeOfType(child, "MemberExpression") || !child.computed || !isNodeOfType(child.property, "Identifier") || child.property.name !== indexName) return;
|
|
47214
|
-
if (findVariableInitializer(child.property, indexName)?.bindingIdentifier === bindingIdentifier && lengthBoundCollections.some((collection) => areSameBoundStaticMemberChains(collection, child.object))) {
|
|
47215
|
-
didFindIndexedCollectionRead = true;
|
|
47216
|
-
return false;
|
|
47217
|
-
}
|
|
47218
|
-
});
|
|
47219
|
-
return didFindIndexedCollectionRead;
|
|
47220
|
-
};
|
|
47221
46718
|
/**
|
|
47222
46719
|
* Resolves whether an identifier is PROVABLY the positional array
|
|
47223
46720
|
* index, by classifying its binding. Per the official rule prompt,
|
|
@@ -47268,12 +46765,6 @@ const resolvePositionalIndexBinding = (identifierNode, depth) => {
|
|
|
47268
46765
|
}
|
|
47269
46766
|
const declarator = binding.bindingIdentifier.parent;
|
|
47270
46767
|
if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declarator.id === binding.bindingIdentifier && declarator.init) {
|
|
47271
|
-
if (isDataIndexedWhileLoopCounter(identifierNode, binding.bindingIdentifier, identifierNode.name)) return {
|
|
47272
|
-
iteratorCall: null,
|
|
47273
|
-
bindingFunction: null,
|
|
47274
|
-
indexParameterPosition: null,
|
|
47275
|
-
isDataIndexedLoopCounter: true
|
|
47276
|
-
};
|
|
47277
46768
|
const initializer = stripParenExpression(declarator.init);
|
|
47278
46769
|
if (isNodeOfType(initializer, "Literal") && typeof initializer.value === "number") {
|
|
47279
46770
|
if (!INDEX_PARAMETER_NAMES.has(identifierNode.name)) return null;
|
|
@@ -47308,101 +46799,6 @@ const iteratorCallExemptsIndexKey = (iteratorCall) => {
|
|
|
47308
46799
|
const receiver = iteratorCall.callee.object;
|
|
47309
46800
|
return isStaticPlaceholderReceiver(receiver) || isFixedMemoReceiver(receiver) || isStaticDefaultLiteralReceiver(receiver) || isStringDerivedReceiver(receiver);
|
|
47310
46801
|
};
|
|
47311
|
-
const isReactNamespaceIdentifier = (node) => {
|
|
47312
|
-
if (!isNodeOfType(node, "Identifier")) return false;
|
|
47313
|
-
const importBinding = getImportBindingForName(node, node.name);
|
|
47314
|
-
const visibleBinding = findVariableInitializer(node, node.name);
|
|
47315
|
-
if (!importBinding) return node.name === "React" && !visibleBinding;
|
|
47316
|
-
return visibleBinding?.initializer?.type.startsWith("Import") === true && importBinding.source === "react" && (importBinding.isNamespace || importBinding.exportedName === "default");
|
|
47317
|
-
};
|
|
47318
|
-
const isReactChildrenObject = (node) => {
|
|
47319
|
-
const candidate = stripParenExpression(node);
|
|
47320
|
-
if (isNodeOfType(candidate, "Identifier")) {
|
|
47321
|
-
const importBinding = getImportBindingForName(candidate, candidate.name);
|
|
47322
|
-
const visibleBinding = findVariableInitializer(candidate, candidate.name);
|
|
47323
|
-
return Boolean(visibleBinding?.initializer?.type === "ImportSpecifier" && importBinding?.source === "react" && importBinding.exportedName === "Children");
|
|
47324
|
-
}
|
|
47325
|
-
return Boolean(isNodeOfType(candidate, "MemberExpression") && !candidate.computed && isReactNamespaceIdentifier(candidate.object) && isNodeOfType(candidate.property, "Identifier") && candidate.property.name === "Children");
|
|
47326
|
-
};
|
|
47327
|
-
const isReactChildrenToArrayCall = (node) => {
|
|
47328
|
-
const candidate = stripParenExpression(node);
|
|
47329
|
-
if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || candidate.callee.computed || !isReactChildrenObject(candidate.callee.object) || !isNodeOfType(candidate.callee.property, "Identifier") || candidate.callee.property.name !== "toArray") return false;
|
|
47330
|
-
const normalizedValueNode = candidate.arguments?.[0];
|
|
47331
|
-
const normalizedValue = normalizedValueNode ? stripParenExpression(normalizedValueNode) : null;
|
|
47332
|
-
if (!normalizedValue || !isNodeOfType(normalizedValue, "Identifier") || normalizedValue.name !== "children") return false;
|
|
47333
|
-
const normalizedBinding = findVariableInitializer(normalizedValue, normalizedValue.name);
|
|
47334
|
-
return Boolean(normalizedBinding && findEnclosingParameter(normalizedBinding.bindingIdentifier));
|
|
47335
|
-
};
|
|
47336
|
-
const isSameIdentifier = (first, second) => {
|
|
47337
|
-
const firstIdentifier = stripParenExpression(first);
|
|
47338
|
-
const secondIdentifier = stripParenExpression(second);
|
|
47339
|
-
if (!isNodeOfType(firstIdentifier, "Identifier") || !isNodeOfType(secondIdentifier, "Identifier") || firstIdentifier.name !== secondIdentifier.name) return false;
|
|
47340
|
-
const firstBinding = findVariableInitializer(firstIdentifier, firstIdentifier.name);
|
|
47341
|
-
const secondBinding = findVariableInitializer(secondIdentifier, secondIdentifier.name);
|
|
47342
|
-
return firstBinding?.bindingIdentifier === secondBinding?.bindingIdentifier;
|
|
47343
|
-
};
|
|
47344
|
-
const isReactChildrenArrayNormalization = (node) => {
|
|
47345
|
-
const candidate = stripParenExpression(node);
|
|
47346
|
-
if (!isNodeOfType(candidate, "ConditionalExpression")) return false;
|
|
47347
|
-
const test = stripParenExpression(candidate.test);
|
|
47348
|
-
if (!isNodeOfType(test, "CallExpression") || !isNodeOfType(test.callee, "MemberExpression") || test.callee.computed || !isNodeOfType(test.callee.object, "Identifier") || test.callee.object.name !== "Array" || findVariableInitializer(test.callee.object, "Array") || !isNodeOfType(test.callee.property, "Identifier") || test.callee.property.name !== "isArray") return false;
|
|
47349
|
-
const testedValueNode = test.arguments?.[0];
|
|
47350
|
-
if (!testedValueNode) return false;
|
|
47351
|
-
const testedValue = stripParenExpression(testedValueNode);
|
|
47352
|
-
if (!isNodeOfType(testedValue, "Identifier")) return false;
|
|
47353
|
-
const testedBinding = findVariableInitializer(testedValue, testedValue.name);
|
|
47354
|
-
if (testedValue.name !== "children" || !testedBinding || !findEnclosingParameter(testedBinding.bindingIdentifier)) return false;
|
|
47355
|
-
const branchWrapsTestedValue = (branch) => {
|
|
47356
|
-
const unwrappedBranch = stripParenExpression(branch);
|
|
47357
|
-
return isNodeOfType(unwrappedBranch, "ArrayExpression") && unwrappedBranch.elements?.length === 1 && Boolean(unwrappedBranch.elements[0] && isSameIdentifier(unwrappedBranch.elements[0], testedValue));
|
|
47358
|
-
};
|
|
47359
|
-
return isSameIdentifier(candidate.consequent, testedValue) && branchWrapsTestedValue(candidate.alternate) || isSameIdentifier(candidate.alternate, testedValue) && branchWrapsTestedValue(candidate.consequent);
|
|
47360
|
-
};
|
|
47361
|
-
const isMutatedEmptyArrayBinding = (identifierNode, depth) => {
|
|
47362
|
-
const binding = findVariableInitializer(identifierNode, identifierNode.name);
|
|
47363
|
-
const initializer = binding?.initializer ? stripParenExpression(binding.initializer) : null;
|
|
47364
|
-
if (!binding || !initializer || !isNodeOfType(initializer, "ArrayExpression") || initializer.elements?.length !== 0) return false;
|
|
47365
|
-
const program = findProgramRoot(identifierNode);
|
|
47366
|
-
if (!program) return false;
|
|
47367
|
-
let didFindReactChildPush = false;
|
|
47368
|
-
walkAst(program, (child) => {
|
|
47369
|
-
if (didFindReactChildPush) return false;
|
|
47370
|
-
if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || child.callee.computed || !isNodeOfType(child.callee.object, "Identifier") || child.callee.object.name !== identifierNode.name || !isNodeOfType(child.callee.property, "Identifier") || child.callee.property.name !== "push") return;
|
|
47371
|
-
if (findVariableInitializer(child.callee.object, identifierNode.name)?.bindingIdentifier === binding.bindingIdentifier && (child.arguments ?? []).some((argument) => {
|
|
47372
|
-
const candidate = stripParenExpression(argument);
|
|
47373
|
-
if (!(isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "JSXElement") || isNodeOfType(candidate, "JSXFragment"))) return false;
|
|
47374
|
-
let doesCarryReactChild = false;
|
|
47375
|
-
walkAst(candidate, (argumentChild) => {
|
|
47376
|
-
if (doesCarryReactChild) return false;
|
|
47377
|
-
if (!isNodeOfType(argumentChild, "Identifier")) return;
|
|
47378
|
-
const declarator = findVariableInitializer(argumentChild, argumentChild.name)?.bindingIdentifier.parent;
|
|
47379
|
-
const declaration = declarator?.parent;
|
|
47380
|
-
const forOfStatement = declaration?.parent;
|
|
47381
|
-
if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declaration && isNodeOfType(declaration, "VariableDeclaration") && forOfStatement && isNodeOfType(forOfStatement, "ForOfStatement") && forOfStatement.left === declaration && isDynamicReactChildrenExpression(forOfStatement.right, depth + 1)) {
|
|
47382
|
-
doesCarryReactChild = true;
|
|
47383
|
-
return false;
|
|
47384
|
-
}
|
|
47385
|
-
});
|
|
47386
|
-
return doesCarryReactChild;
|
|
47387
|
-
})) {
|
|
47388
|
-
didFindReactChildPush = true;
|
|
47389
|
-
return false;
|
|
47390
|
-
}
|
|
47391
|
-
});
|
|
47392
|
-
return didFindReactChildPush;
|
|
47393
|
-
};
|
|
47394
|
-
const isDynamicReactChildrenExpression = (expression, depth) => {
|
|
47395
|
-
if (depth > TYPE_RESOLUTION_DEPTH_LIMIT$2) return false;
|
|
47396
|
-
const candidate = stripParenExpression(expression);
|
|
47397
|
-
if (isReactChildrenToArrayCall(candidate) || isReactChildrenArrayNormalization(candidate)) return true;
|
|
47398
|
-
if (isNodeOfType(candidate, "Identifier")) {
|
|
47399
|
-
if (isMutatedEmptyArrayBinding(candidate, depth)) return true;
|
|
47400
|
-
const binding = findVariableInitializer(candidate, candidate.name);
|
|
47401
|
-
return Boolean(binding?.initializer && isDynamicReactChildrenExpression(binding.initializer, depth + 1));
|
|
47402
|
-
}
|
|
47403
|
-
if (isNodeOfType(candidate, "CallExpression") && isNodeOfType(candidate.callee, "MemberExpression") && !candidate.callee.computed && isNodeOfType(candidate.callee.property, "Identifier") && candidate.callee.property.name === "filter") return isDynamicReactChildrenExpression(candidate.callee.object, depth + 1);
|
|
47404
|
-
return false;
|
|
47405
|
-
};
|
|
47406
46802
|
const resolveKeyTemplateLiteral = (expression) => {
|
|
47407
46803
|
const node = stripParenExpression(expression);
|
|
47408
46804
|
if (isNodeOfType(node, "TemplateLiteral")) return node;
|
|
@@ -47455,19 +46851,28 @@ const findBareItemNamesReferencedByTemplate = (template, itemNames) => {
|
|
|
47455
46851
|
}
|
|
47456
46852
|
return referencedItemNames;
|
|
47457
46853
|
};
|
|
47458
|
-
const
|
|
46854
|
+
const forLoopTestReadsDataLength = (test) => {
|
|
46855
|
+
let didFindLengthRead = false;
|
|
46856
|
+
walkAst(test, (child) => {
|
|
46857
|
+
if (didFindLengthRead) return false;
|
|
46858
|
+
if (isNodeOfType(child, "MemberExpression") && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
|
|
46859
|
+
didFindLengthRead = true;
|
|
46860
|
+
return false;
|
|
46861
|
+
}
|
|
46862
|
+
});
|
|
46863
|
+
return didFindLengthRead;
|
|
46864
|
+
};
|
|
46865
|
+
const isNumericForLoopCounter = (attributeNode, indexName) => {
|
|
47459
46866
|
const binding = findVariableInitializer(attributeNode, indexName);
|
|
47460
46867
|
if (!binding) return false;
|
|
47461
46868
|
const declarator = binding.bindingIdentifier.parent;
|
|
47462
46869
|
if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return false;
|
|
47463
46870
|
const declaration = declarator.parent;
|
|
47464
46871
|
if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return false;
|
|
47465
|
-
if (!declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
|
|
47466
46872
|
const forStatement = declaration.parent;
|
|
47467
|
-
if (forStatement
|
|
47468
|
-
|
|
47469
|
-
|
|
47470
|
-
return !loopTestBoundsCounterByLength(whileLoop.test, indexName, binding.bindingIdentifier);
|
|
46873
|
+
if (!forStatement || !isNodeOfType(forStatement, "ForStatement") || forStatement.init !== declaration || !declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
|
|
46874
|
+
if (forStatement.test && forLoopTestReadsDataLength(forStatement.test)) return false;
|
|
46875
|
+
return true;
|
|
47471
46876
|
};
|
|
47472
46877
|
const EMPTY_NAME_SET$1 = /* @__PURE__ */ new Set();
|
|
47473
46878
|
const findIteratorItemNamesOfBinding = (binding) => {
|
|
@@ -47501,11 +46906,11 @@ const collectDerivedRowContentNames = (bindingFunction, itemNames) => {
|
|
|
47501
46906
|
* observable harm; anything stateful — form controls, media, custom
|
|
47502
46907
|
* components, unknown calls — keeps the diagnostic.
|
|
47503
46908
|
*/
|
|
47504
|
-
const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames
|
|
46909
|
+
const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames) => {
|
|
47505
46910
|
const jsxElement = openingElement.parent;
|
|
47506
46911
|
if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement")) return false;
|
|
47507
46912
|
const children = jsxElement.children ?? [];
|
|
47508
|
-
const bareIdentifierNames = children.some((child) => isNodeOfType(child, "JSXElement")) ? derivedNames :
|
|
46913
|
+
const bareIdentifierNames = children.some((child) => isNodeOfType(child, "JSXElement")) ? derivedNames : new Set([...derivedNames, ...itemNames]);
|
|
47509
46914
|
return children.some((child) => containsStatefulDescendant(child, {
|
|
47510
46915
|
memberRootNames: itemNames,
|
|
47511
46916
|
allowAnyMemberRead: true,
|
|
@@ -47513,15 +46918,6 @@ const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames, ar
|
|
|
47513
46918
|
callCalleeRootNames: itemNames
|
|
47514
46919
|
}));
|
|
47515
46920
|
};
|
|
47516
|
-
const elementHasDirectItemChild = (openingElement, itemNames) => {
|
|
47517
|
-
const jsxElement = openingElement.parent;
|
|
47518
|
-
if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement")) return false;
|
|
47519
|
-
return (jsxElement.children ?? []).some((child) => {
|
|
47520
|
-
if (!isNodeOfType(child, "JSXExpressionContainer")) return false;
|
|
47521
|
-
const expression = stripParenExpression(child.expression);
|
|
47522
|
-
return isNodeOfType(expression, "Identifier") && itemNames.has(expression.name);
|
|
47523
|
-
});
|
|
47524
|
-
};
|
|
47525
46921
|
const callbackFiltersRows = (bindingFunction) => {
|
|
47526
46922
|
if (!bindingFunction) return false;
|
|
47527
46923
|
let didFindNullReturn = false;
|
|
@@ -47573,21 +46969,19 @@ const noArrayIndexAsKey = defineRule({
|
|
|
47573
46969
|
const indexUse = findPositionalIndexUse(node.value.expression, 0);
|
|
47574
46970
|
if (!indexUse) return;
|
|
47575
46971
|
const indexName = indexUse.identifier.name;
|
|
47576
|
-
if (
|
|
46972
|
+
if (isNumericForLoopCounter(node, indexName)) return;
|
|
47577
46973
|
if (indexUse.binding.iteratorCall && iteratorCallExemptsIndexKey(indexUse.binding.iteratorCall)) return;
|
|
47578
46974
|
const keyTemplate = resolveKeyTemplateLiteral(node.value.expression);
|
|
47579
46975
|
if (keyTemplate && templateHasOuterMemberIdentity(keyTemplate, indexUse.binding.bindingFunction)) return;
|
|
47580
|
-
if (hasAriaHiddenAncestor(node)
|
|
46976
|
+
if (hasAriaHiddenAncestor(node)) return;
|
|
47581
46977
|
const itemNames = findIteratorItemNamesOfBinding(indexUse.binding);
|
|
47582
46978
|
const derivedNames = collectDerivedRowContentNames(indexUse.binding.bindingFunction, itemNames);
|
|
47583
|
-
const iteratorCallee = indexUse.binding.iteratorCall?.callee;
|
|
47584
|
-
const hasDynamicReactChildren = Boolean(iteratorCallee && isNodeOfType(iteratorCallee, "MemberExpression") && isDynamicReactChildrenExpression(iteratorCallee.object, 0));
|
|
47585
46979
|
const openingElement = node.parent;
|
|
47586
46980
|
if (openingElement && isNodeOfType(openingElement, "JSXOpeningElement")) {
|
|
47587
46981
|
const elementName = openingElement.name;
|
|
47588
46982
|
if (isNodeOfType(elementName, "JSXIdentifier")) {
|
|
47589
46983
|
if (elementName.name === "Fragment") {
|
|
47590
|
-
if (!fragmentHasStatefulChildren(openingElement, itemNames, derivedNames
|
|
46984
|
+
if (!fragmentHasStatefulChildren(openingElement, itemNames, derivedNames)) return;
|
|
47591
46985
|
} else if (PURE_SVG_PRIMITIVE_TAGS.has(elementName.name)) {
|
|
47592
46986
|
if (!callbackFiltersRows(indexUse.binding.bindingFunction)) return;
|
|
47593
46987
|
} else if (STATELESS_HTML_LEAF_TAGS.has(elementName.name)) {
|
|
@@ -47595,14 +46989,14 @@ const noArrayIndexAsKey = defineRule({
|
|
|
47595
46989
|
if (jsxElement && isNodeOfType(jsxElement, "JSXElement")) {
|
|
47596
46990
|
const isInlineTextRun = INLINE_TEXT_LEAF_TAGS.has(elementName.name);
|
|
47597
46991
|
const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET$1;
|
|
47598
|
-
if (!
|
|
46992
|
+
if (!containsStatefulDescendant(jsxElement, {
|
|
47599
46993
|
memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET$1,
|
|
47600
46994
|
bareIdentifierNames: primitiveItemNames.size > 0 ? new Set([...derivedNames, ...primitiveItemNames]) : derivedNames
|
|
47601
|
-
}))
|
|
46995
|
+
})) return;
|
|
47602
46996
|
}
|
|
47603
46997
|
}
|
|
47604
46998
|
}
|
|
47605
|
-
if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && isNodeOfType(elementName.property, "JSXIdentifier") && elementName.object.name === "React" && elementName.property.name === "Fragment" && !fragmentHasStatefulChildren(openingElement, itemNames, derivedNames
|
|
46999
|
+
if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && isNodeOfType(elementName.property, "JSXIdentifier") && elementName.object.name === "React" && elementName.property.name === "Fragment" && !fragmentHasStatefulChildren(openingElement, itemNames, derivedNames)) return;
|
|
47606
47000
|
}
|
|
47607
47001
|
context.report({
|
|
47608
47002
|
node,
|
|
@@ -64498,106 +63892,6 @@ const isGatedByFalsyInitialState = (node, scopes) => {
|
|
|
64498
63892
|
};
|
|
64499
63893
|
//#endregion
|
|
64500
63894
|
//#region src/plugin/rules/performance/no-hydration-branch-on-browser-global.ts
|
|
64501
|
-
const findGuardingIfStatements = (node, functionBoundary) => {
|
|
64502
|
-
const guardingIfStatements = [];
|
|
64503
|
-
let currentNode = node.parent;
|
|
64504
|
-
while (currentNode && currentNode !== functionBoundary) {
|
|
64505
|
-
if (isNodeOfType(currentNode, "IfStatement")) guardingIfStatements.push(currentNode);
|
|
64506
|
-
currentNode = currentNode.parent;
|
|
64507
|
-
}
|
|
64508
|
-
return guardingIfStatements;
|
|
64509
|
-
};
|
|
64510
|
-
const doesNodeReadSymbol = (node, symbol) => {
|
|
64511
|
-
let doesReadSymbol = false;
|
|
64512
|
-
walkAst(node, (childNode) => {
|
|
64513
|
-
if (isNodeOfType(childNode, "Identifier") && symbol.references.some((reference) => reference.identifier === childNode && reference.flag !== "write")) {
|
|
64514
|
-
doesReadSymbol = true;
|
|
64515
|
-
return false;
|
|
64516
|
-
}
|
|
64517
|
-
});
|
|
64518
|
-
return doesReadSymbol;
|
|
64519
|
-
};
|
|
64520
|
-
const collectWrittenSymbols = (node, scopes) => {
|
|
64521
|
-
const writtenSymbols = /* @__PURE__ */ new Set();
|
|
64522
|
-
walkAst(node, (childNode) => {
|
|
64523
|
-
if (childNode !== node && isFunctionLike$1(childNode)) return false;
|
|
64524
|
-
if (!isNodeOfType(childNode, "Identifier")) return;
|
|
64525
|
-
const reference = scopes.referenceFor(childNode);
|
|
64526
|
-
if (!reference || reference.flag === "read" || !reference.resolvedSymbol) return;
|
|
64527
|
-
writtenSymbols.add(reference.resolvedSymbol);
|
|
64528
|
-
});
|
|
64529
|
-
return writtenSymbols;
|
|
64530
|
-
};
|
|
64531
|
-
const isDescendantOf = (node, ancestorNode) => {
|
|
64532
|
-
let currentNode = node.parent;
|
|
64533
|
-
while (currentNode) {
|
|
64534
|
-
if (currentNode === ancestorNode) return true;
|
|
64535
|
-
currentNode = currentNode.parent;
|
|
64536
|
-
}
|
|
64537
|
-
return false;
|
|
64538
|
-
};
|
|
64539
|
-
const getAssignedValue = (identifier) => {
|
|
64540
|
-
const assignmentExpression = identifier.parent;
|
|
64541
|
-
return isNodeOfType(assignmentExpression, "AssignmentExpression") && assignmentExpression.operator === "=" && assignmentExpression.left === identifier ? assignmentExpression.right : null;
|
|
64542
|
-
};
|
|
64543
|
-
const doesGuardPreserveInitialSymbolValue = (symbol, guardingIfStatement, scopes) => {
|
|
64544
|
-
const initialValue = symbol.initializer;
|
|
64545
|
-
if (!initialValue) return false;
|
|
64546
|
-
const guardedWrites = symbol.references.filter((reference) => reference.flag !== "read" && isDescendantOf(reference.identifier, guardingIfStatement));
|
|
64547
|
-
return guardedWrites.length > 0 && guardedWrites.every((reference) => {
|
|
64548
|
-
const assignedValue = getAssignedValue(reference.identifier);
|
|
64549
|
-
return Boolean(assignedValue && areExpressionsStructurallyEqual(initialValue, assignedValue) && doEquivalentExpressionBindingsMatch(initialValue, assignedValue, scopes));
|
|
64550
|
-
});
|
|
64551
|
-
};
|
|
64552
|
-
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));
|
|
64553
|
-
const isUnconditionalOrStaticallySelected = (node, context) => {
|
|
64554
|
-
if (context.cfg.isUnconditionalFromEntry(node)) return true;
|
|
64555
|
-
let currentNode = node;
|
|
64556
|
-
let outermostStaticIfStatement = null;
|
|
64557
|
-
let parentNode = currentNode.parent;
|
|
64558
|
-
while (parentNode) {
|
|
64559
|
-
if (isFunctionLike$1(parentNode)) break;
|
|
64560
|
-
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
64561
|
-
const staticResult = readInitialStateBoolean(parentNode.test, context.scopes);
|
|
64562
|
-
let selectedBranch = null;
|
|
64563
|
-
if (staticResult === true) selectedBranch = parentNode.consequent;
|
|
64564
|
-
if (staticResult === false) selectedBranch = parentNode.alternate;
|
|
64565
|
-
if (!selectedBranch || currentNode !== selectedBranch && !isDescendantOf(currentNode, selectedBranch)) return false;
|
|
64566
|
-
outermostStaticIfStatement = parentNode;
|
|
64567
|
-
}
|
|
64568
|
-
currentNode = parentNode;
|
|
64569
|
-
parentNode = currentNode.parent;
|
|
64570
|
-
}
|
|
64571
|
-
return Boolean(outermostStaticIfStatement && context.cfg.isUnconditionalFromEntry(outermostStaticIfStatement));
|
|
64572
|
-
};
|
|
64573
|
-
const containsExplicitReactRuntimeReference = (node, scopes) => {
|
|
64574
|
-
let hasRuntimeReference = false;
|
|
64575
|
-
walkAst(node, (childNode) => {
|
|
64576
|
-
if (isNodeOfType(childNode, "ImportDeclaration") && typeof childNode.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(childNode.source.value)) {
|
|
64577
|
-
hasRuntimeReference = true;
|
|
64578
|
-
return false;
|
|
64579
|
-
}
|
|
64580
|
-
if (!isNodeOfType(childNode, "CallExpression")) return;
|
|
64581
|
-
const sourceArgument = (childNode.arguments ?? [])[0];
|
|
64582
|
-
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;
|
|
64583
|
-
hasRuntimeReference = true;
|
|
64584
|
-
return false;
|
|
64585
|
-
});
|
|
64586
|
-
return hasRuntimeReference;
|
|
64587
|
-
};
|
|
64588
|
-
const findComponentRenderingLocalFunctionResult = (functionNode, scopes) => {
|
|
64589
|
-
const bindingIdentifier = getDirectFunctionBindingIdentifier(functionNode);
|
|
64590
|
-
if (!isNodeOfType(bindingIdentifier, "Identifier")) return null;
|
|
64591
|
-
const functionSymbol = scopes.symbolFor(bindingIdentifier);
|
|
64592
|
-
if (!functionSymbol) return null;
|
|
64593
|
-
for (const reference of functionSymbol.references) {
|
|
64594
|
-
const callExpression = reference.identifier.parent;
|
|
64595
|
-
if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== reference.identifier) continue;
|
|
64596
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(callExpression, scopes);
|
|
64597
|
-
if (componentOrHookNode && isInRenderedOutput(callExpression, componentOrHookNode, scopes)) return componentOrHookNode;
|
|
64598
|
-
}
|
|
64599
|
-
return null;
|
|
64600
|
-
};
|
|
64601
63895
|
const evaluateEquality$1 = (operator, left, right) => {
|
|
64602
63896
|
if (operator === "===" || operator === "==") return left === right;
|
|
64603
63897
|
if (operator === "!==" || operator === "!=") return left !== right;
|
|
@@ -64648,107 +63942,6 @@ const readLogicalConditionResult = (operator, leftResult, rightResult) => {
|
|
|
64648
63942
|
if (leftResult === false && rightResult === false) return false;
|
|
64649
63943
|
return null;
|
|
64650
63944
|
};
|
|
64651
|
-
const areLooselyEqualPrimitiveResults = (left, right) => {
|
|
64652
|
-
if (left.kind === right.kind) return left.value === right.value;
|
|
64653
|
-
if (left.kind === "null" && right.kind === "undefined" || left.kind === "undefined" && right.kind === "null") return true;
|
|
64654
|
-
if (left.kind === "boolean") return areLooselyEqualPrimitiveResults({
|
|
64655
|
-
kind: "number",
|
|
64656
|
-
value: left.value ? 1 : 0
|
|
64657
|
-
}, right);
|
|
64658
|
-
if (right.kind === "boolean") return areLooselyEqualPrimitiveResults(left, {
|
|
64659
|
-
kind: "number",
|
|
64660
|
-
value: right.value ? 1 : 0
|
|
64661
|
-
});
|
|
64662
|
-
if (left.kind === "number" && right.kind === "string") return left.value === Number(right.value);
|
|
64663
|
-
if (left.kind === "string" && right.kind === "number") return Number(left.value) === right.value;
|
|
64664
|
-
return false;
|
|
64665
|
-
};
|
|
64666
|
-
const readHydrationPrimitiveResult = (expression, context, runtime, state) => {
|
|
64667
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
64668
|
-
const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
|
|
64669
|
-
if (predicateMatch) return {
|
|
64670
|
-
kind: "boolean",
|
|
64671
|
-
value: predicateMatch[`${runtime}Result`]
|
|
64672
|
-
};
|
|
64673
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) {
|
|
64674
|
-
const value = unwrappedExpression.value;
|
|
64675
|
-
if (value === null) return {
|
|
64676
|
-
kind: "null",
|
|
64677
|
-
value
|
|
64678
|
-
};
|
|
64679
|
-
if (typeof value === "boolean") return {
|
|
64680
|
-
kind: "boolean",
|
|
64681
|
-
value
|
|
64682
|
-
};
|
|
64683
|
-
if (typeof value === "number") return {
|
|
64684
|
-
kind: "number",
|
|
64685
|
-
value
|
|
64686
|
-
};
|
|
64687
|
-
if (typeof value === "string") return {
|
|
64688
|
-
kind: "string",
|
|
64689
|
-
value
|
|
64690
|
-
};
|
|
64691
|
-
return null;
|
|
64692
|
-
}
|
|
64693
|
-
if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return {
|
|
64694
|
-
kind: "undefined",
|
|
64695
|
-
value: void 0
|
|
64696
|
-
};
|
|
64697
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
64698
|
-
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
64699
|
-
const parameterValue = symbol ? state.parameterValuesBySymbolId.get(symbol.id) : null;
|
|
64700
|
-
if (symbol && parameterValue && !state.visitedSymbolIds.has(symbol.id)) {
|
|
64701
|
-
state.visitedSymbolIds.add(symbol.id);
|
|
64702
|
-
const result = readHydrationPrimitiveResult(parameterValue, context, runtime, state);
|
|
64703
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64704
|
-
return result;
|
|
64705
|
-
}
|
|
64706
|
-
if (symbol && symbol.kind === "const" && symbol.initializer && symbol.references.every((reference) => reference.flag === "read") && !state.visitedSymbolIds.has(symbol.id)) {
|
|
64707
|
-
state.visitedSymbolIds.add(symbol.id);
|
|
64708
|
-
const result = readHydrationPrimitiveResult(symbol.initializer, context, runtime, state);
|
|
64709
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64710
|
-
return result;
|
|
64711
|
-
}
|
|
64712
|
-
}
|
|
64713
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
|
|
64714
|
-
const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
|
|
64715
|
-
return argumentResult === null ? null : {
|
|
64716
|
-
kind: "boolean",
|
|
64717
|
-
value: !argumentResult
|
|
64718
|
-
};
|
|
64719
|
-
}
|
|
64720
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
64721
|
-
const leftResult = readHydrationPrimitiveResult(unwrappedExpression.left, context, runtime, state);
|
|
64722
|
-
const rightResult = readHydrationPrimitiveResult(unwrappedExpression.right, context, runtime, state);
|
|
64723
|
-
if (!leftResult || !rightResult) return null;
|
|
64724
|
-
if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
|
|
64725
|
-
const areEqual = leftResult.kind === rightResult.kind && leftResult.value === rightResult.value;
|
|
64726
|
-
return {
|
|
64727
|
-
kind: "boolean",
|
|
64728
|
-
value: unwrappedExpression.operator === "===" ? areEqual : !areEqual
|
|
64729
|
-
};
|
|
64730
|
-
}
|
|
64731
|
-
if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
|
|
64732
|
-
const areEqual = areLooselyEqualPrimitiveResults(leftResult, rightResult);
|
|
64733
|
-
return {
|
|
64734
|
-
kind: "boolean",
|
|
64735
|
-
value: unwrappedExpression.operator === "==" ? areEqual : !areEqual
|
|
64736
|
-
};
|
|
64737
|
-
}
|
|
64738
|
-
}
|
|
64739
|
-
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
64740
|
-
const callArguments = unwrappedExpression.arguments ?? [];
|
|
64741
|
-
const callee = stripParenExpression(unwrappedExpression.callee);
|
|
64742
|
-
if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) {
|
|
64743
|
-
const argumentResult = readHydrationConditionResult(callArguments[0], context, runtime, state);
|
|
64744
|
-
return argumentResult === null ? null : {
|
|
64745
|
-
kind: "boolean",
|
|
64746
|
-
value: argumentResult
|
|
64747
|
-
};
|
|
64748
|
-
}
|
|
64749
|
-
}
|
|
64750
|
-
return null;
|
|
64751
|
-
};
|
|
64752
63945
|
const readHydrationConditionResult = (expression, context, runtime, state) => {
|
|
64753
63946
|
const unwrappedExpression = stripParenExpression(expression);
|
|
64754
63947
|
const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
|
|
@@ -64797,10 +63990,6 @@ const readHydrationConditionResult = (expression, context, runtime, state) => {
|
|
|
64797
63990
|
parameterValuesBySymbolId
|
|
64798
63991
|
});
|
|
64799
63992
|
}
|
|
64800
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
64801
|
-
const result = readHydrationPrimitiveResult(unwrappedExpression, context, runtime, state);
|
|
64802
|
-
return result?.kind === "boolean" && typeof result.value === "boolean" ? result.value : null;
|
|
64803
|
-
}
|
|
64804
63993
|
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
|
|
64805
63994
|
const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
|
|
64806
63995
|
return argumentResult === null ? null : !argumentResult;
|
|
@@ -64861,19 +64050,13 @@ const doEquivalentExpressionBindingsMatch = (leftExpression, rightExpression, sc
|
|
|
64861
64050
|
const rightSymbol = scopes.symbolFor(right);
|
|
64862
64051
|
return leftSymbol || rightSymbol ? leftSymbol?.id === rightSymbol?.id : true;
|
|
64863
64052
|
}
|
|
64864
|
-
|
|
64865
|
-
|
|
64866
|
-
|
|
64867
|
-
|
|
64868
|
-
|
|
64869
|
-
|
|
64870
|
-
|
|
64871
|
-
}
|
|
64872
|
-
if (!Array.isArray(leftValue)) continue;
|
|
64873
|
-
if (!Array.isArray(rightValue)) return false;
|
|
64874
|
-
const leftNodes = leftValue.filter(isAstNode);
|
|
64875
|
-
const rightNodes = rightValue.filter(isAstNode);
|
|
64876
|
-
if (leftNodes.length !== rightNodes.length || leftNodes.some((leftNode, index) => !rightNodes[index] || !doEquivalentExpressionBindingsMatch(leftNode, rightNodes[index], scopes))) return false;
|
|
64053
|
+
if (isNodeOfType(left, "MemberExpression") && isNodeOfType(right, "MemberExpression")) return doEquivalentExpressionBindingsMatch(left.object, right.object, scopes) && (!left.computed || doEquivalentExpressionBindingsMatch(left.property, right.property, scopes));
|
|
64054
|
+
if (isNodeOfType(left, "CallExpression") && isNodeOfType(right, "CallExpression")) {
|
|
64055
|
+
const rightArguments = right.arguments ?? [];
|
|
64056
|
+
return doEquivalentExpressionBindingsMatch(left.callee, right.callee, scopes) && (left.arguments ?? []).every((argument, index) => {
|
|
64057
|
+
const rightArgument = rightArguments[index];
|
|
64058
|
+
return Boolean(rightArgument && doEquivalentExpressionBindingsMatch(argument, rightArgument, scopes));
|
|
64059
|
+
});
|
|
64877
64060
|
}
|
|
64878
64061
|
return true;
|
|
64879
64062
|
};
|
|
@@ -64887,57 +64070,6 @@ const doHelperReturnValuesDiffer = (leftValues, rightValues, context) => {
|
|
|
64887
64070
|
const everyValueHasEquivalent = (values, candidateValues) => values.every((value) => candidateValues.some((candidateValue) => areHelperReturnValuesEquivalent(value, candidateValue, context)));
|
|
64888
64071
|
return !everyValueHasEquivalent(leftValues, rightValues) || !everyValueHasEquivalent(rightValues, leftValues);
|
|
64889
64072
|
};
|
|
64890
|
-
const isExpressionProvablyReflexive = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
64891
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
64892
|
-
if (matchBrowserPredicate(unwrappedExpression, context)) return true;
|
|
64893
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) return typeof unwrappedExpression.value !== "number" || !Number.isNaN(unwrappedExpression.value);
|
|
64894
|
-
if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return true;
|
|
64895
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
64896
|
-
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
64897
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer) return false;
|
|
64898
|
-
visitedSymbolIds.add(symbol.id);
|
|
64899
|
-
const assignedValues = symbol.references.filter((reference) => reference.flag !== "read").map((reference) => getAssignedValue(reference.identifier));
|
|
64900
|
-
const isReflexive = isExpressionProvablyReflexive(symbol.initializer, context, visitedSymbolIds) && assignedValues.every((assignedValue) => Boolean(assignedValue && isExpressionProvablyReflexive(assignedValue, context, visitedSymbolIds)));
|
|
64901
|
-
visitedSymbolIds.delete(symbol.id);
|
|
64902
|
-
return isReflexive;
|
|
64903
|
-
}
|
|
64904
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isExpressionProvablyReflexive(unwrappedExpression.consequent, context, visitedSymbolIds) && isExpressionProvablyReflexive(unwrappedExpression.alternate, context, visitedSymbolIds);
|
|
64905
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && (unwrappedExpression.operator === "!" || unwrappedExpression.operator === "typeof" || unwrappedExpression.operator === "void")) return true;
|
|
64906
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) return unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==" || unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=";
|
|
64907
|
-
if (isNodeOfType(unwrappedExpression, "ArrayExpression") || isNodeOfType(unwrappedExpression, "ObjectExpression") || isNodeOfType(unwrappedExpression, "FunctionExpression") || isNodeOfType(unwrappedExpression, "ArrowFunctionExpression") || isNodeOfType(unwrappedExpression, "TemplateLiteral")) return true;
|
|
64908
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
|
|
64909
|
-
const callee = stripParenExpression(unwrappedExpression.callee);
|
|
64910
|
-
return isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee);
|
|
64911
|
-
};
|
|
64912
|
-
const getReturnedObjectPropertyValues = (node, propertyName, scopes) => {
|
|
64913
|
-
if (isNodeOfType(node, "ReturnStatement")) return node.argument ? getReturnedObjectPropertyValues(node.argument, propertyName, scopes) : [];
|
|
64914
|
-
if (isNodeOfType(node, "ObjectExpression")) return node.properties.flatMap((property) => isNodeOfType(property, "Property") && property.kind === "init" && getResolvedStaticPropertyName(property, scopes) === propertyName ? [property.value] : []);
|
|
64915
|
-
if (isNodeOfType(node, "IfStatement")) return [...getReturnedObjectPropertyValues(node.consequent, propertyName, scopes), ...node.alternate ? getReturnedObjectPropertyValues(node.alternate, propertyName, scopes) : []];
|
|
64916
|
-
if (isNodeOfType(node, "TryStatement")) return [
|
|
64917
|
-
...getReturnedObjectPropertyValues(node.block, propertyName, scopes),
|
|
64918
|
-
...node.handler ? getReturnedObjectPropertyValues(node.handler.body, propertyName, scopes) : [],
|
|
64919
|
-
...node.finalizer ? getReturnedObjectPropertyValues(node.finalizer, propertyName, scopes) : []
|
|
64920
|
-
];
|
|
64921
|
-
if (!isNodeOfType(node, "BlockStatement")) return [];
|
|
64922
|
-
const propertyValues = [];
|
|
64923
|
-
for (const childStatement of node.body) {
|
|
64924
|
-
propertyValues.push(...getReturnedObjectPropertyValues(childStatement, propertyName, scopes));
|
|
64925
|
-
if (statementAlwaysExits$1(childStatement)) break;
|
|
64926
|
-
}
|
|
64927
|
-
return propertyValues;
|
|
64928
|
-
};
|
|
64929
|
-
const matchHydrationFunctionPropertyResult = (functionNode, propertyName, context, state) => {
|
|
64930
|
-
if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
|
|
64931
|
-
state.visitedFunctionNodes.add(functionNode);
|
|
64932
|
-
const propertyValues = getReturnedObjectPropertyValues(functionNode.body, propertyName, context.scopes);
|
|
64933
|
-
let match = null;
|
|
64934
|
-
for (const propertyValue of propertyValues) {
|
|
64935
|
-
match = matchHydrationConditionInternal(propertyValue, context, state);
|
|
64936
|
-
if (match) break;
|
|
64937
|
-
}
|
|
64938
|
-
state.visitedFunctionNodes.delete(functionNode);
|
|
64939
|
-
return match;
|
|
64940
|
-
};
|
|
64941
64073
|
const matchHydrationConditionInternal = (expression, context, state) => {
|
|
64942
64074
|
const unwrappedExpression = stripParenExpression(expression);
|
|
64943
64075
|
const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
|
|
@@ -64954,90 +64086,14 @@ const matchHydrationConditionInternal = (expression, context, state) => {
|
|
|
64954
64086
|
state.visitedSymbolIds.delete(symbol.id);
|
|
64955
64087
|
return match;
|
|
64956
64088
|
}
|
|
64957
|
-
if (symbol && (symbol.kind === "let" || symbol.kind === "var") && !state.visitedSymbolIds.has(symbol.id)) {
|
|
64958
|
-
state.visitedSymbolIds.add(symbol.id);
|
|
64959
|
-
if (symbol.initializer && symbol.references.every((reference) => reference.flag === "read")) {
|
|
64960
|
-
const match = matchHydrationConditionInternal(symbol.initializer, context, state);
|
|
64961
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64962
|
-
return match;
|
|
64963
|
-
}
|
|
64964
|
-
for (const reference of symbol.references) {
|
|
64965
|
-
if (reference.flag === "read") continue;
|
|
64966
|
-
if (!isNodeReachableWithinFunction(reference.identifier, context)) continue;
|
|
64967
|
-
const enclosingFunction = findEnclosingFunction$1(reference.identifier);
|
|
64968
|
-
if (!enclosingFunction) continue;
|
|
64969
|
-
for (const guardingIfStatement of findGuardingIfStatements(reference.identifier, enclosingFunction)) {
|
|
64970
|
-
if (doesGuardPreserveInitialSymbolValue(symbol, guardingIfStatement, context.scopes) || isWriteOverwrittenBefore(symbol, reference.identifier, guardingIfStatement, unwrappedExpression, context)) continue;
|
|
64971
|
-
const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
|
|
64972
|
-
if (match) {
|
|
64973
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64974
|
-
return match;
|
|
64975
|
-
}
|
|
64976
|
-
}
|
|
64977
|
-
}
|
|
64978
|
-
const readingFunction = findEnclosingFunction$1(unwrappedExpression);
|
|
64979
|
-
for (const reference of symbol.references) {
|
|
64980
|
-
if (reference.flag === "read") continue;
|
|
64981
|
-
const writingFunction = findEnclosingFunction$1(reference.identifier);
|
|
64982
|
-
if (!readingFunction || !isFunctionLike$1(writingFunction) || writingFunction === readingFunction || writingFunction.async || writingFunction.params.length > 0 || isNodeOfType(writingFunction, "FunctionDeclaration") && writingFunction.generator || isNodeOfType(writingFunction, "FunctionExpression") && writingFunction.generator) continue;
|
|
64983
|
-
const assignedValue = getAssignedValue(reference.identifier);
|
|
64984
|
-
if (symbol.initializer && assignedValue && areExpressionsStructurallyEqual(symbol.initializer, assignedValue) && doEquivalentExpressionBindingsMatch(symbol.initializer, assignedValue, context.scopes)) continue;
|
|
64985
|
-
const functionBinding = getDirectFunctionBindingIdentifier(writingFunction);
|
|
64986
|
-
if (!isNodeOfType(functionBinding, "Identifier")) continue;
|
|
64987
|
-
const functionSymbol = context.scopes.symbolFor(functionBinding);
|
|
64988
|
-
if (!functionSymbol) continue;
|
|
64989
|
-
for (const functionReference of functionSymbol.references) {
|
|
64990
|
-
const callExpression = functionReference.identifier.parent;
|
|
64991
|
-
if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== functionReference.identifier || (callExpression.arguments ?? []).length > 0 || findEnclosingFunction$1(callExpression) !== readingFunction || !isNodeReachableWithinFunction(callExpression, context) || getNodeStartIndex(callExpression) >= getNodeStartIndex(unwrappedExpression)) continue;
|
|
64992
|
-
for (const guardingIfStatement of findGuardingIfStatements(callExpression, readingFunction)) {
|
|
64993
|
-
if (isWriteOverwrittenBefore(symbol, callExpression, guardingIfStatement, unwrappedExpression, context)) continue;
|
|
64994
|
-
const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
|
|
64995
|
-
if (match) {
|
|
64996
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
64997
|
-
return match;
|
|
64998
|
-
}
|
|
64999
|
-
}
|
|
65000
|
-
}
|
|
65001
|
-
}
|
|
65002
|
-
state.visitedSymbolIds.delete(symbol.id);
|
|
65003
|
-
}
|
|
65004
64089
|
if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || state.visitedSymbolIds.has(symbol.id)) return null;
|
|
65005
64090
|
state.visitedSymbolIds.add(symbol.id);
|
|
65006
64091
|
const match = matchHydrationConditionInternal(symbol.initializer, context, state);
|
|
65007
64092
|
state.visitedSymbolIds.delete(symbol.id);
|
|
65008
64093
|
return match;
|
|
65009
64094
|
}
|
|
65010
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression")) {
|
|
65011
|
-
const propertyName = getResolvedStaticPropertyName(unwrappedExpression, context.scopes, {
|
|
65012
|
-
allowConstNumericLiteral: true,
|
|
65013
|
-
stringifyNonStringLiterals: true
|
|
65014
|
-
});
|
|
65015
|
-
const object = stripParenExpression(unwrappedExpression.object);
|
|
65016
|
-
if (propertyName === null || !isNodeOfType(object, "CallExpression")) return null;
|
|
65017
|
-
const callArguments = object.arguments ?? [];
|
|
65018
|
-
if (isReactApiCall(object, "useMemo", context.scopes, {
|
|
65019
|
-
allowGlobalReactNamespace: true,
|
|
65020
|
-
resolveNamedAliases: true
|
|
65021
|
-
})) {
|
|
65022
|
-
const callbackArgument = callArguments[0];
|
|
65023
|
-
if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
|
|
65024
|
-
const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
|
|
65025
|
-
return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? matchHydrationFunctionPropertyResult(callbackFunction, propertyName, context, state) : null;
|
|
65026
|
-
}
|
|
65027
|
-
const helperFunction = resolveExactLocalFunction(object.callee, context.scopes);
|
|
65028
|
-
return isFunctionLike$1(helperFunction) && helperFunction.params.length === 0 && callArguments.length === 0 ? matchHydrationFunctionPropertyResult(helperFunction, propertyName, context, state) : null;
|
|
65029
|
-
}
|
|
65030
64095
|
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
65031
64096
|
const callArguments = unwrappedExpression.arguments ?? [];
|
|
65032
|
-
if (isReactApiCall(unwrappedExpression, "useState", context.scopes, {
|
|
65033
|
-
allowGlobalReactNamespace: true,
|
|
65034
|
-
resolveNamedAliases: true
|
|
65035
|
-
})) {
|
|
65036
|
-
const initialState = callArguments[0];
|
|
65037
|
-
if (!initialState || isNodeOfType(initialState, "SpreadElement")) return null;
|
|
65038
|
-
const lazyInitializer = resolveExactLocalFunction(initialState, context.scopes);
|
|
65039
|
-
return isFunctionLike$1(lazyInitializer) && lazyInitializer.params.length === 0 ? matchHydrationFunctionResult(lazyInitializer, context, state) : matchHydrationConditionInternal(initialState, context, state);
|
|
65040
|
-
}
|
|
65041
64097
|
if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
|
|
65042
64098
|
allowGlobalReactNamespace: true,
|
|
65043
64099
|
resolveNamedAliases: true
|
|
@@ -65065,22 +64121,6 @@ const matchHydrationConditionInternal = (expression, context, state) => {
|
|
|
65065
64121
|
});
|
|
65066
64122
|
}
|
|
65067
64123
|
if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationConditionInternal(unwrappedExpression.argument, context, state);
|
|
65068
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
65069
|
-
const staticTestResult = readInitialStateBoolean(unwrappedExpression.test, context.scopes);
|
|
65070
|
-
if (staticTestResult !== null) return matchHydrationConditionInternal(staticTestResult ? unwrappedExpression.consequent : unwrappedExpression.alternate, context, state);
|
|
65071
|
-
return matchHydrationConditionInternal(unwrappedExpression.test, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.consequent, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.alternate, context, state);
|
|
65072
|
-
}
|
|
65073
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
65074
|
-
if (unwrappedExpression.operator !== "===" && unwrappedExpression.operator !== "!==" && unwrappedExpression.operator !== "==" && unwrappedExpression.operator !== "!=") return null;
|
|
65075
|
-
const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
|
|
65076
|
-
const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
|
|
65077
|
-
const nestedMatch = leftMatch ?? rightMatch;
|
|
65078
|
-
if (!nestedMatch) return null;
|
|
65079
|
-
const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client", state);
|
|
65080
|
-
const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server", state);
|
|
65081
|
-
if (clientResult !== null && serverResult !== null) return clientResult !== serverResult ? nestedMatch : null;
|
|
65082
|
-
return leftMatch && rightMatch && areExpressionsStructurallyEqual(unwrappedExpression.left, unwrappedExpression.right) && doEquivalentExpressionBindingsMatch(unwrappedExpression.left, unwrappedExpression.right, context.scopes) && isExpressionProvablyReflexive(unwrappedExpression.left, context) ? null : nestedMatch;
|
|
65083
|
-
}
|
|
65084
64124
|
if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
|
|
65085
64125
|
const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
|
|
65086
64126
|
const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
|
|
@@ -65097,13 +64137,8 @@ const matchHydrationReturningStatement = (statement, context, state) => {
|
|
|
65097
64137
|
const consequentValues = getReturnedValues(statement.consequent);
|
|
65098
64138
|
const alternateValues = statement.alternate ? getReturnedValues(statement.alternate) : findFollowingReturnedValues(statement);
|
|
65099
64139
|
if (conditionMatch && consequentValues.length > 0 && alternateValues.length > 0 && doHelperReturnValuesDiffer(consequentValues, alternateValues, context)) return conditionMatch;
|
|
65100
|
-
if (conditionMatch) {
|
|
65101
|
-
const followingReturnedValues = findFollowingReturnedValues(statement);
|
|
65102
|
-
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;
|
|
65103
|
-
}
|
|
65104
64140
|
return matchHydrationReturningStatement(statement.consequent, context, state) ?? (statement.alternate ? matchHydrationReturningStatement(statement.alternate, context, state) : null);
|
|
65105
64141
|
}
|
|
65106
|
-
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);
|
|
65107
64142
|
if (!isNodeOfType(statement, "BlockStatement")) return null;
|
|
65108
64143
|
for (const childStatement of statement.body) {
|
|
65109
64144
|
const match = matchHydrationReturningStatement(childStatement, context, state);
|
|
@@ -65124,62 +64159,47 @@ const matchHydrationCondition = (expression, context) => matchHydrationCondition
|
|
|
65124
64159
|
visitedFunctionNodes: /* @__PURE__ */ new Set(),
|
|
65125
64160
|
visitedSymbolIds: /* @__PURE__ */ new Set()
|
|
65126
64161
|
});
|
|
65127
|
-
const areNodeArraysEquivalent = (leftNodes, rightNodes
|
|
65128
|
-
const areRenderedBranchesEquivalent = (leftNode, rightNode
|
|
64162
|
+
const areNodeArraysEquivalent = (leftNodes, rightNodes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index]));
|
|
64163
|
+
const areRenderedBranchesEquivalent = (leftNode, rightNode) => {
|
|
65129
64164
|
if (!leftNode || !rightNode) return leftNode === rightNode;
|
|
65130
64165
|
const left = stripParenExpression(leftNode);
|
|
65131
64166
|
const right = stripParenExpression(rightNode);
|
|
65132
|
-
if (areExpressionsStructurallyEqual(left, right)) return
|
|
64167
|
+
if (areExpressionsStructurallyEqual(left, right)) return true;
|
|
65133
64168
|
if (left.type !== right.type) return false;
|
|
65134
64169
|
if (isNodeOfType(left, "JSXText") && isNodeOfType(right, "JSXText")) return left.value === right.value;
|
|
65135
64170
|
if (isNodeOfType(left, "JSXExpressionContainer") && isNodeOfType(right, "JSXExpressionContainer")) {
|
|
65136
64171
|
if (!isAstNode(left.expression) || !isAstNode(right.expression)) return left.expression.type === right.expression.type;
|
|
65137
|
-
return areRenderedBranchesEquivalent(left.expression, right.expression
|
|
64172
|
+
return areRenderedBranchesEquivalent(left.expression, right.expression);
|
|
65138
64173
|
}
|
|
65139
64174
|
if (isNodeOfType(left, "JSXElement") && isNodeOfType(right, "JSXElement")) {
|
|
65140
64175
|
if (flattenJsxName$1(left.openingElement.name) !== flattenJsxName$1(right.openingElement.name)) return false;
|
|
65141
|
-
if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes
|
|
65142
|
-
return areNodeArraysEquivalent(left.children, right.children
|
|
64176
|
+
if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes)) return false;
|
|
64177
|
+
return areNodeArraysEquivalent(left.children, right.children);
|
|
65143
64178
|
}
|
|
65144
|
-
if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children
|
|
64179
|
+
if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children);
|
|
65145
64180
|
if (isNodeOfType(left, "JSXAttribute") && isNodeOfType(right, "JSXAttribute")) {
|
|
65146
64181
|
if (flattenJsxName$1(left.name) !== flattenJsxName$1(right.name)) return false;
|
|
65147
|
-
return areRenderedBranchesEquivalent(left.value, right.value
|
|
64182
|
+
return areRenderedBranchesEquivalent(left.value, right.value);
|
|
65148
64183
|
}
|
|
65149
|
-
if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument
|
|
64184
|
+
if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument);
|
|
65150
64185
|
if (isNodeOfType(left, "TemplateLiteral") && isNodeOfType(right, "TemplateLiteral")) {
|
|
65151
64186
|
if (left.quasis.length !== right.quasis.length) return false;
|
|
65152
64187
|
if (!left.quasis.every((quasi, index) => quasi.value.cooked === right.quasis[index]?.value.cooked && quasi.value.raw === right.quasis[index]?.value.raw)) return false;
|
|
65153
|
-
return areNodeArraysEquivalent(left.expressions, right.expressions
|
|
64188
|
+
return areNodeArraysEquivalent(left.expressions, right.expressions);
|
|
65154
64189
|
}
|
|
65155
64190
|
return false;
|
|
65156
64191
|
};
|
|
65157
|
-
const
|
|
65158
|
-
if (isReactApiCall(node, "createElement", scopes, {
|
|
65159
|
-
allowGlobalReactNamespace: true,
|
|
65160
|
-
resolveNamedAliases: true
|
|
65161
|
-
})) return true;
|
|
65162
|
-
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
65163
|
-
const callee = stripParenExpression(node.callee);
|
|
65164
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "createElement") return false;
|
|
65165
|
-
const receiver = stripParenExpression(callee.object);
|
|
65166
|
-
const namespaceIdentifier = isNodeOfType(receiver, "MemberExpression") && !receiver.computed && isNodeOfType(receiver.property, "Identifier") && receiver.property.name === "default" ? stripParenExpression(receiver.object) : receiver;
|
|
65167
|
-
if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
|
|
65168
|
-
const namespaceSymbol = scopes.symbolFor(namespaceIdentifier);
|
|
65169
|
-
return Boolean(namespaceSymbol?.initializer && namespaceSymbol.references.every((reference) => reference.flag === "read") && containsExplicitReactRuntimeReference(namespaceSymbol.initializer, scopes));
|
|
65170
|
-
};
|
|
65171
|
-
const isRenderedValue = (node, scopes) => {
|
|
64192
|
+
const isRenderedValue = (node) => {
|
|
65172
64193
|
const unwrappedNode = stripParenExpression(node);
|
|
65173
64194
|
if (isNodeOfType(unwrappedNode, "Literal")) return unwrappedNode.value !== null && unwrappedNode.value !== true && unwrappedNode.value !== false && unwrappedNode.value !== "";
|
|
65174
64195
|
if (isNodeOfType(unwrappedNode, "TemplateLiteral")) return unwrappedNode.expressions.length > 0 || unwrappedNode.quasis[0]?.value.cooked !== "";
|
|
65175
|
-
if (isNodeOfType(unwrappedNode, "CallExpression")) return isProvenReactCreateElementCall(unwrappedNode, scopes);
|
|
65176
64196
|
return isNodeOfType(unwrappedNode, "JSXElement") || isNodeOfType(unwrappedNode, "JSXFragment");
|
|
65177
64197
|
};
|
|
65178
|
-
const findRenderedValueInAndBranch = (node
|
|
64198
|
+
const findRenderedValueInAndBranch = (node) => {
|
|
65179
64199
|
const unwrappedNode = stripParenExpression(node);
|
|
65180
|
-
if (
|
|
64200
|
+
if (isRenderedValue(unwrappedNode)) return unwrappedNode;
|
|
65181
64201
|
if (!isNodeOfType(unwrappedNode, "LogicalExpression") || unwrappedNode.operator !== "&&") return null;
|
|
65182
|
-
return findRenderedValueInAndBranch(unwrappedNode.right
|
|
64202
|
+
return findRenderedValueInAndBranch(unwrappedNode.right);
|
|
65183
64203
|
};
|
|
65184
64204
|
const findEnclosingJsxAttribute = (node) => {
|
|
65185
64205
|
let currentNode = node.parent;
|
|
@@ -65212,11 +64232,6 @@ const getReturnedValues = (statement) => {
|
|
|
65212
64232
|
if (!statement) return [];
|
|
65213
64233
|
if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? [statement.argument] : [];
|
|
65214
64234
|
if (isNodeOfType(statement, "IfStatement")) return [...getReturnedValues(statement.consequent), ...getReturnedValues(statement.alternate)];
|
|
65215
|
-
if (isNodeOfType(statement, "TryStatement")) return [
|
|
65216
|
-
...getReturnedValues(statement.block),
|
|
65217
|
-
...getReturnedValues(statement.handler?.body),
|
|
65218
|
-
...getReturnedValues(statement.finalizer)
|
|
65219
|
-
];
|
|
65220
64235
|
if (!isNodeOfType(statement, "BlockStatement")) return [];
|
|
65221
64236
|
const returnedValues = [];
|
|
65222
64237
|
for (const childStatement of statement.body) {
|
|
@@ -65225,127 +64240,6 @@ const getReturnedValues = (statement) => {
|
|
|
65225
64240
|
}
|
|
65226
64241
|
return returnedValues;
|
|
65227
64242
|
};
|
|
65228
|
-
const isPotentiallyRenderedValueInternal = (node, scopes, visitedFunctionNodes) => {
|
|
65229
|
-
const unwrappedNode = stripParenExpression(node);
|
|
65230
|
-
if (isRenderedValue(unwrappedNode, scopes)) return true;
|
|
65231
|
-
if (isNodeOfType(unwrappedNode, "ConditionalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.consequent, scopes, visitedFunctionNodes) && isPotentiallyRenderedValueInternal(unwrappedNode.alternate, scopes, visitedFunctionNodes);
|
|
65232
|
-
if (isNodeOfType(unwrappedNode, "LogicalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.right, scopes, visitedFunctionNodes);
|
|
65233
|
-
if (!isNodeOfType(unwrappedNode, "CallExpression")) return false;
|
|
65234
|
-
const calledFunction = resolveExactLocalFunction(unwrappedNode.callee, scopes);
|
|
65235
|
-
if (!isFunctionLike$1(calledFunction) || visitedFunctionNodes.has(calledFunction)) return false;
|
|
65236
|
-
visitedFunctionNodes.add(calledFunction);
|
|
65237
|
-
const returnedValues = isNodeOfType(calledFunction.body, "BlockStatement") ? getReturnedValues(calledFunction.body) : [calledFunction.body];
|
|
65238
|
-
const isPotentiallyRendered = returnedValues.length > 0 && returnedValues.every((returnedValue) => isPotentiallyRenderedValueInternal(returnedValue, scopes, visitedFunctionNodes));
|
|
65239
|
-
visitedFunctionNodes.delete(calledFunction);
|
|
65240
|
-
return isPotentiallyRendered;
|
|
65241
|
-
};
|
|
65242
|
-
const isPotentiallyRenderedValue = (node, scopes) => isPotentiallyRenderedValueInternal(node, scopes, /* @__PURE__ */ new Set());
|
|
65243
|
-
const findUseStateBindingSymbol = (node, componentOrHookNode, scopes) => {
|
|
65244
|
-
let currentNode = node.parent;
|
|
65245
|
-
while (currentNode && currentNode !== componentOrHookNode) {
|
|
65246
|
-
if (isNodeOfType(currentNode, "CallExpression") && isReactApiCall(currentNode, "useState", scopes, {
|
|
65247
|
-
allowGlobalReactNamespace: true,
|
|
65248
|
-
resolveNamedAliases: true
|
|
65249
|
-
}) && isNodeOfType(currentNode.parent, "VariableDeclarator") && isNodeOfType(currentNode.parent.id, "ArrayPattern")) {
|
|
65250
|
-
const stateBinding = currentNode.parent.id.elements?.[0];
|
|
65251
|
-
return isNodeOfType(stateBinding, "Identifier") ? scopes.symbolFor(stateBinding) : null;
|
|
65252
|
-
}
|
|
65253
|
-
if (isFunctionLike$1(currentNode)) return null;
|
|
65254
|
-
currentNode = currentNode.parent;
|
|
65255
|
-
}
|
|
65256
|
-
return null;
|
|
65257
|
-
};
|
|
65258
|
-
const doesReferenceControlStructuralRenderedValue = (referenceIdentifier) => {
|
|
65259
|
-
let currentNode = referenceIdentifier;
|
|
65260
|
-
let parentNode = currentNode.parent;
|
|
65261
|
-
while (parentNode) {
|
|
65262
|
-
if (isNodeOfType(parentNode, "ConditionalExpression") && parentNode.test === currentNode && (isStructuralRenderedValue(parentNode.consequent) || isStructuralRenderedValue(parentNode.alternate))) return true;
|
|
65263
|
-
if (isNodeOfType(parentNode, "LogicalExpression") && parentNode.left === currentNode && isStructuralRenderedValue(parentNode.right)) return true;
|
|
65264
|
-
if (isNodeOfType(parentNode, "JSXExpressionContainer") || isFunctionLike$1(parentNode)) return false;
|
|
65265
|
-
currentNode = parentNode;
|
|
65266
|
-
parentNode = currentNode.parent;
|
|
65267
|
-
}
|
|
65268
|
-
return false;
|
|
65269
|
-
};
|
|
65270
|
-
const isRenderedHydrationConsumer = (node, producerHookNode, scopes) => {
|
|
65271
|
-
const renderingComponent = findRenderPhaseComponentOrHook(node, scopes);
|
|
65272
|
-
return Boolean(renderingComponent && renderingComponent !== producerHookNode && isInRenderedOutput(node, renderingComponent, scopes) && !isGatedByFalsyInitialState(node, scopes) && !isAfterClientOnlyEarlyReturn(node, renderingComponent, scopes) && (!hasSuppressHydrationWarningAttribute(findEnclosingJsxOpeningElement(node)) || doesReferenceControlStructuralRenderedValue(node)));
|
|
65273
|
-
};
|
|
65274
|
-
const doesConsumerExpressionReachRenderedOutput = (node, producerHookNode, scopes, visitedSymbolIds) => {
|
|
65275
|
-
if (isRenderedHydrationConsumer(node, producerHookNode, scopes)) return true;
|
|
65276
|
-
const parentNode = node.parent;
|
|
65277
|
-
if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== node || !isNodeOfType(parentNode.id, "Identifier")) return false;
|
|
65278
|
-
const aliasSymbol = scopes.symbolFor(parentNode.id);
|
|
65279
|
-
if (!aliasSymbol || visitedSymbolIds.has(aliasSymbol.id)) return false;
|
|
65280
|
-
visitedSymbolIds.add(aliasSymbol.id);
|
|
65281
|
-
const doesReachRenderedOutput = aliasSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, visitedSymbolIds));
|
|
65282
|
-
visitedSymbolIds.delete(aliasSymbol.id);
|
|
65283
|
-
return doesReachRenderedOutput;
|
|
65284
|
-
};
|
|
65285
|
-
const doesConsumerBindingReachRenderedOutput = (bindingIdentifier, producerHookNode, scopes) => {
|
|
65286
|
-
if (!isNodeOfType(bindingIdentifier, "Identifier")) return false;
|
|
65287
|
-
const consumerSymbol = scopes.symbolFor(bindingIdentifier);
|
|
65288
|
-
if (!consumerSymbol) return false;
|
|
65289
|
-
return consumerSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, new Set([consumerSymbol.id])));
|
|
65290
|
-
};
|
|
65291
|
-
const getReturnedStatePaths = (returnedValue, stateSymbol, scopes) => {
|
|
65292
|
-
const unwrappedValue = stripParenExpression(returnedValue);
|
|
65293
|
-
if (isNodeOfType(unwrappedValue, "ObjectExpression")) return unwrappedValue.properties.flatMap((property) => {
|
|
65294
|
-
if (!isNodeOfType(property, "Property") || property.kind !== "init" || !doesNodeReadSymbol(property.value, stateSymbol)) return [];
|
|
65295
|
-
const propertyName = getResolvedStaticPropertyName(property, scopes);
|
|
65296
|
-
return propertyName === null ? [] : [{
|
|
65297
|
-
kind: "property",
|
|
65298
|
-
key: propertyName
|
|
65299
|
-
}];
|
|
65300
|
-
});
|
|
65301
|
-
if (isNodeOfType(unwrappedValue, "ArrayExpression")) return (unwrappedValue.elements ?? []).flatMap((element, index) => element && isAstNode(element) && doesNodeReadSymbol(element, stateSymbol) ? [{
|
|
65302
|
-
kind: "index",
|
|
65303
|
-
key: String(index)
|
|
65304
|
-
}] : []);
|
|
65305
|
-
return doesNodeReadSymbol(unwrappedValue, stateSymbol) ? [{
|
|
65306
|
-
kind: "direct",
|
|
65307
|
-
key: null
|
|
65308
|
-
}] : [];
|
|
65309
|
-
};
|
|
65310
|
-
const doesCallResultPathReachRenderedOutput = (callExpression, returnedStatePath, producerHookNode, scopes) => {
|
|
65311
|
-
const callParent = callExpression.parent;
|
|
65312
|
-
if (returnedStatePath.kind === "direct") return doesConsumerExpressionReachRenderedOutput(callExpression, producerHookNode, scopes, /* @__PURE__ */ new Set());
|
|
65313
|
-
if (isNodeOfType(callParent, "MemberExpression") && callParent.object === callExpression && getResolvedStaticPropertyName(callParent, scopes, {
|
|
65314
|
-
allowConstNumericLiteral: true,
|
|
65315
|
-
stringifyNonStringLiterals: true
|
|
65316
|
-
}) === returnedStatePath.key) return doesConsumerExpressionReachRenderedOutput(callParent, producerHookNode, scopes, /* @__PURE__ */ new Set());
|
|
65317
|
-
if (!isNodeOfType(callParent, "VariableDeclarator") || callParent.init !== callExpression) return false;
|
|
65318
|
-
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));
|
|
65319
|
-
if (returnedStatePath.kind === "index" && isNodeOfType(callParent.id, "ArrayPattern")) {
|
|
65320
|
-
const element = callParent.id.elements?.[Number(returnedStatePath.key)];
|
|
65321
|
-
return Boolean(element && doesConsumerBindingReachRenderedOutput(element, producerHookNode, scopes));
|
|
65322
|
-
}
|
|
65323
|
-
if (!isNodeOfType(callParent.id, "Identifier")) return false;
|
|
65324
|
-
const resultSymbol = scopes.symbolFor(callParent.id);
|
|
65325
|
-
if (!resultSymbol) return false;
|
|
65326
|
-
return resultSymbol.references.some((reference) => {
|
|
65327
|
-
const memberExpression = reference.identifier.parent;
|
|
65328
|
-
return Boolean(isNodeOfType(memberExpression, "MemberExpression") && memberExpression.object === reference.identifier && getResolvedStaticPropertyName(memberExpression, scopes, {
|
|
65329
|
-
allowConstNumericLiteral: true,
|
|
65330
|
-
stringifyNonStringLiterals: true
|
|
65331
|
-
}) === returnedStatePath.key && doesConsumerExpressionReachRenderedOutput(memberExpression, producerHookNode, scopes, new Set([resultSymbol.id])));
|
|
65332
|
-
});
|
|
65333
|
-
};
|
|
65334
|
-
const isReturnedUseStateInitializerRendered = (node, componentOrHookNode, scopes) => {
|
|
65335
|
-
if (!isFunctionLike$1(componentOrHookNode)) return false;
|
|
65336
|
-
const stateSymbol = findUseStateBindingSymbol(node, componentOrHookNode, scopes);
|
|
65337
|
-
if (!stateSymbol) return false;
|
|
65338
|
-
const returnedStatePaths = (isNodeOfType(componentOrHookNode.body, "BlockStatement") ? getReturnedValues(componentOrHookNode.body) : [componentOrHookNode.body]).flatMap((returnedValue) => getReturnedStatePaths(returnedValue, stateSymbol, scopes));
|
|
65339
|
-
if (returnedStatePaths.length === 0) return false;
|
|
65340
|
-
const functionBinding = getDirectFunctionBindingIdentifier(componentOrHookNode);
|
|
65341
|
-
if (!isNodeOfType(functionBinding, "Identifier")) return false;
|
|
65342
|
-
const functionSymbol = scopes.symbolFor(functionBinding);
|
|
65343
|
-
if (!functionSymbol) return false;
|
|
65344
|
-
return functionSymbol.references.some((functionReference) => {
|
|
65345
|
-
const callExpression = functionReference.identifier.parent;
|
|
65346
|
-
return Boolean(isNodeOfType(callExpression, "CallExpression") && callExpression.callee === functionReference.identifier && returnedStatePaths.some((returnedStatePath) => doesCallResultPathReachRenderedOutput(callExpression, returnedStatePath, componentOrHookNode, scopes)));
|
|
65347
|
-
});
|
|
65348
|
-
};
|
|
65349
64243
|
const findFollowingReturnedValues = (ifStatement) => {
|
|
65350
64244
|
const parentNode = ifStatement.parent;
|
|
65351
64245
|
if (!isNodeOfType(parentNode, "BlockStatement")) return [];
|
|
@@ -65358,24 +64252,24 @@ const findFollowingReturnedValues = (ifStatement) => {
|
|
|
65358
64252
|
}
|
|
65359
64253
|
return returnedValues;
|
|
65360
64254
|
};
|
|
65361
|
-
const areConditionExpressionsEquivalent = (leftExpression, rightExpression
|
|
64255
|
+
const areConditionExpressionsEquivalent = (leftExpression, rightExpression) => {
|
|
65362
64256
|
const left = stripParenExpression(leftExpression);
|
|
65363
64257
|
const right = stripParenExpression(rightExpression);
|
|
65364
|
-
if (areExpressionsStructurallyEqual(left, right)) return
|
|
64258
|
+
if (areExpressionsStructurallyEqual(left, right)) return true;
|
|
65365
64259
|
if (left.type !== right.type) return false;
|
|
65366
|
-
if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument
|
|
65367
|
-
if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left
|
|
65368
|
-
if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left
|
|
64260
|
+
if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument);
|
|
64261
|
+
if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
|
|
64262
|
+
if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
|
|
65369
64263
|
return false;
|
|
65370
64264
|
};
|
|
65371
|
-
const areReturnTreesEquivalent = (leftStatement, rightStatement
|
|
64265
|
+
const areReturnTreesEquivalent = (leftStatement, rightStatement) => {
|
|
65372
64266
|
if (!leftStatement || !rightStatement) return leftStatement === rightStatement;
|
|
65373
|
-
if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument
|
|
65374
|
-
if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test
|
|
64267
|
+
if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument);
|
|
64268
|
+
if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate);
|
|
65375
64269
|
if (!isNodeOfType(leftStatement, "BlockStatement") || !isNodeOfType(rightStatement, "BlockStatement")) return false;
|
|
65376
64270
|
const leftReturningStatements = leftStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
|
|
65377
64271
|
const rightReturningStatements = rightStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
|
|
65378
|
-
return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]
|
|
64272
|
+
return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]));
|
|
65379
64273
|
};
|
|
65380
64274
|
const isStructuralRenderedValue = (node) => {
|
|
65381
64275
|
if (!node) return false;
|
|
@@ -65399,22 +64293,19 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
|
|
|
65399
64293
|
if (isTestlikeFilename(context.filename)) return {};
|
|
65400
64294
|
if (classifyReactNativeFileTarget(context) === "react-native") return {};
|
|
65401
64295
|
let fileHasUseClientDirective = false;
|
|
65402
|
-
let fileHasExplicitReactRuntimeReference = false;
|
|
65403
64296
|
let fileIsEmailTemplate = false;
|
|
65404
64297
|
const reportedNodes = /* @__PURE__ */ new Set();
|
|
65405
|
-
const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext
|
|
64298
|
+
const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext) => {
|
|
65406
64299
|
const conditionMatch = matchHydrationCondition(conditionNode, context);
|
|
65407
64300
|
if (!conditionMatch) return;
|
|
65408
64301
|
const { predicateMatch, predicateNode } = conditionMatch;
|
|
65409
64302
|
if (reportedNodes.has(predicateNode)) return;
|
|
65410
|
-
if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch
|
|
65411
|
-
const
|
|
65412
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
|
|
64303
|
+
if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch)) return;
|
|
64304
|
+
const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes);
|
|
65413
64305
|
if (!componentOrHookNode) return;
|
|
65414
|
-
|
|
65415
|
-
if (!
|
|
65416
|
-
if (
|
|
65417
|
-
if (!hasProvenRenderedConsumer && !(requiresRenderedContext ? isPotentiallyRenderedValue(leftBranch, context.scopes) : isRenderedValue(leftBranch, context.scopes)) && (!rightBranch || !(requiresRenderedContext ? isPotentiallyRenderedValue(rightBranch, context.scopes) : isRenderedValue(rightBranch, context.scopes)))) {
|
|
64306
|
+
if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
|
|
64307
|
+
if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes)) return;
|
|
64308
|
+
if (!isRenderedValue(leftBranch) && (!rightBranch || !isRenderedValue(rightBranch))) {
|
|
65418
64309
|
const attribute = findEnclosingJsxAttribute(conditionNode);
|
|
65419
64310
|
if (!attribute || isEventHandlerAttribute(attribute)) return;
|
|
65420
64311
|
}
|
|
@@ -65433,31 +64324,28 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
|
|
|
65433
64324
|
return {
|
|
65434
64325
|
Program(node) {
|
|
65435
64326
|
fileHasUseClientDirective = hasDirective(node, "use client");
|
|
65436
|
-
fileHasExplicitReactRuntimeReference = containsExplicitReactRuntimeReference(node, context.scopes);
|
|
65437
64327
|
fileIsEmailTemplate = hasEmailTemplateImport(node);
|
|
65438
64328
|
},
|
|
65439
64329
|
ConditionalExpression(node) {
|
|
65440
64330
|
reportHydrationBranch(node.test, node.consequent, node.alternate, true);
|
|
65441
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(node, context.scopes);
|
|
65442
|
-
if (componentOrHookNode && isReturnedUseStateInitializerRendered(node, componentOrHookNode, context.scopes)) reportHydrationBranch(node.test, node.consequent, node.alternate, false, true);
|
|
65443
64331
|
},
|
|
65444
64332
|
LogicalExpression(node) {
|
|
65445
64333
|
if (node.operator !== "&&" && node.operator !== "||") return;
|
|
65446
|
-
const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right
|
|
64334
|
+
const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right) : isRenderedValue(node.right) ? node.right : null;
|
|
65447
64335
|
if (!renderedValue) return;
|
|
65448
64336
|
reportHydrationBranch(node, renderedValue, null, true);
|
|
65449
64337
|
},
|
|
65450
64338
|
IfStatement(node) {
|
|
65451
|
-
if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate
|
|
64339
|
+
if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate)) return;
|
|
65452
64340
|
const consequentValues = getReturnedValues(node.consequent);
|
|
65453
64341
|
const alternateValues = node.alternate ? getReturnedValues(node.alternate) : findFollowingReturnedValues(node);
|
|
65454
64342
|
if (consequentValues.length === 0 || alternateValues.length === 0) return;
|
|
65455
|
-
const
|
|
65456
|
-
const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
|
|
64343
|
+
const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
|
|
65457
64344
|
if (!componentOrHookNode) return;
|
|
65458
|
-
|
|
64345
|
+
const enclosingFunction = findEnclosingFunction$1(node);
|
|
64346
|
+
if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
|
|
65459
64347
|
for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
|
|
65460
|
-
if (!isRenderedValue(consequentValue
|
|
64348
|
+
if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
|
|
65461
64349
|
reportHydrationBranch(node.test, consequentValue, alternateValue, false);
|
|
65462
64350
|
}
|
|
65463
64351
|
}
|
|
@@ -74998,29 +73886,6 @@ const doesPredicateTruthRequireMatch = (matchCall, predicateFunction) => {
|
|
|
74998
73886
|
}
|
|
74999
73887
|
return !isNegated && predicateFunction.body === child;
|
|
75000
73888
|
};
|
|
75001
|
-
const doesPredicateReturnNormalizedMatch = (matchCall, predicateFunction) => {
|
|
75002
|
-
if (!isFunctionLike$1(predicateFunction) || !isNodeOfType(predicateFunction.body, "BlockStatement") || predicateFunction.body.body.length !== 2 || !isNodeOfType(predicateFunction.body.body[0], "VariableDeclaration")) return false;
|
|
75003
|
-
const returnStatement = predicateFunction.body.body[1];
|
|
75004
|
-
if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
|
|
75005
|
-
let negationCount = 0;
|
|
75006
|
-
let expression = matchCall;
|
|
75007
|
-
let parent = expression.parent ?? null;
|
|
75008
|
-
while (parent && parent !== returnStatement) {
|
|
75009
|
-
if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") {
|
|
75010
|
-
negationCount += 1;
|
|
75011
|
-
expression = parent;
|
|
75012
|
-
parent = parent.parent ?? null;
|
|
75013
|
-
continue;
|
|
75014
|
-
}
|
|
75015
|
-
if (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) || isNodeOfType(parent, "ChainExpression")) {
|
|
75016
|
-
expression = parent;
|
|
75017
|
-
parent = parent.parent ?? null;
|
|
75018
|
-
continue;
|
|
75019
|
-
}
|
|
75020
|
-
return false;
|
|
75021
|
-
}
|
|
75022
|
-
return parent === returnStatement && returnStatement.argument === expression && negationCount % 2 === 0;
|
|
75023
|
-
};
|
|
75024
73889
|
const isStringTypeofGuardForPath = (test, expectedPath) => {
|
|
75025
73890
|
const target = stripParenExpression(test);
|
|
75026
73891
|
if (!isNodeOfType(target, "BinaryExpression") || target.operator !== "===") return false;
|
|
@@ -75061,26 +73926,6 @@ const pathUsesOptionalAccess = (node) => {
|
|
|
75061
73926
|
current = current.object;
|
|
75062
73927
|
}
|
|
75063
73928
|
};
|
|
75064
|
-
const getNormalizedClassNameRoot = (expression) => {
|
|
75065
|
-
const conditional = stripParenExpression(expression);
|
|
75066
|
-
if (!isNodeOfType(conditional, "ConditionalExpression")) return null;
|
|
75067
|
-
const consequent = stripParenExpression(conditional.consequent);
|
|
75068
|
-
const rootIdentifier = getRootIdentifier(consequent);
|
|
75069
|
-
if (!rootIdentifier || receiverPathKey(consequent) !== `${rootIdentifier.name}.className`) return null;
|
|
75070
|
-
const test = stripParenExpression(conditional.test);
|
|
75071
|
-
if (!isNodeOfType(test, "BinaryExpression") || test.operator !== "===") return null;
|
|
75072
|
-
const testOperands = [test.left, test.right].map((operand) => stripParenExpression(operand));
|
|
75073
|
-
const typeofOperand = testOperands.find((operand) => isNodeOfType(operand, "UnaryExpression"));
|
|
75074
|
-
const stringOperand = testOperands.find((operand) => isNodeOfType(operand, "Literal"));
|
|
75075
|
-
if (!typeofOperand || !isNodeOfType(typeofOperand, "UnaryExpression") || typeofOperand.operator !== "typeof" || receiverPathKey(typeofOperand.argument) !== `${rootIdentifier.name}.className` || !stringOperand || !isNodeOfType(stringOperand, "Literal") || stringOperand.value !== "string") return null;
|
|
75076
|
-
const alternate = stripParenExpression(conditional.alternate);
|
|
75077
|
-
if (!isNodeOfType(alternate, "LogicalExpression") || alternate.operator !== "??") return null;
|
|
75078
|
-
const fallback = stripParenExpression(alternate.right);
|
|
75079
|
-
const attributeCall = stripParenExpression(alternate.left);
|
|
75080
|
-
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;
|
|
75081
|
-
const attributeName = attributeCall.arguments[0] ? stripParenExpression(attributeCall.arguments[0]) : null;
|
|
75082
|
-
return attributeName && isNodeOfType(attributeName, "Literal") && attributeName.value === "class" ? rootIdentifier : null;
|
|
75083
|
-
};
|
|
75084
73929
|
const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
|
|
75085
73930
|
const resultIdentifier = getRootIdentifier(matchReceiver);
|
|
75086
73931
|
const resultPath = receiverPathKey(matchReceiver);
|
|
@@ -75089,17 +73934,11 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
|
|
|
75089
73934
|
if (!isDirectFinderMatchReturn(assertion) && !isOptionalResultPath) return false;
|
|
75090
73935
|
const resultSymbol = context.scopes.symbolFor(resultIdentifier);
|
|
75091
73936
|
const initializer = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
|
|
75092
|
-
if (resultSymbol?.kind !== "const" || !initializer) return false;
|
|
75093
|
-
const finderCall = isNodeOfType(initializer, "CallExpression") ? initializer : isNodeOfType(initializer, "ConditionalExpression") ? (() => {
|
|
75094
|
-
const alternate = stripParenExpression(initializer.alternate);
|
|
75095
|
-
const consequent = stripParenExpression(initializer.consequent);
|
|
75096
|
-
return (isNodeOfType(alternate, "Literal") && alternate.value === null || isNodeOfType(alternate, "Identifier") && alternate.name === "undefined" && context.scopes.isGlobalReference(alternate)) && isNodeOfType(consequent, "CallExpression") ? consequent : null;
|
|
75097
|
-
})() : null;
|
|
75098
|
-
if (!finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
|
|
73937
|
+
if (resultSymbol?.kind !== "const" || !initializer || !isNodeOfType(initializer, "CallExpression")) return false;
|
|
75099
73938
|
if (!isOptionalResultPath && !isImmediatelyGuardedFinderResult(assertion, resultSymbol, resultPath, context)) return false;
|
|
75100
|
-
const finderCallee = stripParenExpression(
|
|
73939
|
+
const finderCallee = stripParenExpression(initializer.callee);
|
|
75101
73940
|
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;
|
|
75102
|
-
const predicateArgument =
|
|
73941
|
+
const predicateArgument = initializer.arguments[1];
|
|
75103
73942
|
if (!predicateArgument) return false;
|
|
75104
73943
|
const predicateFunction = resolveExactLocalFunction(predicateArgument, context.scopes);
|
|
75105
73944
|
if (!predicateFunction || !isFunctionLike$1(predicateFunction)) return false;
|
|
@@ -75123,43 +73962,6 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
|
|
|
75123
73962
|
});
|
|
75124
73963
|
return didProveMatch;
|
|
75125
73964
|
};
|
|
75126
|
-
const isMatchProvenByNormalizedFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
|
|
75127
|
-
const normalizedReceiver = stripParenExpression(matchReceiver);
|
|
75128
|
-
if (!isNodeOfType(normalizedReceiver, "Identifier")) return false;
|
|
75129
|
-
const normalizedReceiverSymbol = context.scopes.symbolFor(normalizedReceiver);
|
|
75130
|
-
const normalizedReceiverInitializer = normalizedReceiverSymbol?.initializer ? stripParenExpression(normalizedReceiverSymbol.initializer) : null;
|
|
75131
|
-
const resultIdentifier = normalizedReceiverInitializer ? getNormalizedClassNameRoot(normalizedReceiverInitializer) : null;
|
|
75132
|
-
if (normalizedReceiverSymbol?.kind !== "const" || normalizedReceiverSymbol.references.some((reference) => reference.flag !== "read") || !resultIdentifier) return false;
|
|
75133
|
-
const resultSymbol = context.scopes.symbolFor(resultIdentifier);
|
|
75134
|
-
const finderCall = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
|
|
75135
|
-
if (resultSymbol?.kind !== "const" || resultSymbol.references.some((reference) => reference.flag !== "read") || !finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
|
|
75136
|
-
const finderCallee = stripParenExpression(finderCall.callee);
|
|
75137
|
-
if (!isNodeOfType(finderCallee, "Identifier") || context.scopes.symbolFor(finderCallee)?.kind !== "import" || getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) !== "findUpUntil") return false;
|
|
75138
|
-
if (!isPresenceProvenBeforeNode(assertion, (test) => {
|
|
75139
|
-
const expression = stripParenExpression(test);
|
|
75140
|
-
return isNodeOfType(expression, "Identifier") && context.scopes.symbolFor(expression)?.id === resultSymbol.id;
|
|
75141
|
-
})) return false;
|
|
75142
|
-
const predicateArgument = finderCall.arguments[1];
|
|
75143
|
-
const predicateFunction = predicateArgument ? resolveExactLocalFunction(predicateArgument, context.scopes) : null;
|
|
75144
|
-
if (!predicateFunction || !isFunctionLike$1(predicateFunction) || predicateFunction.async || predicateFunction.generator) return false;
|
|
75145
|
-
const predicateParameter = predicateFunction.params[0];
|
|
75146
|
-
if (!isNodeOfType(predicateParameter, "Identifier")) return false;
|
|
75147
|
-
let didProveNormalizedMatch = false;
|
|
75148
|
-
walkAst(predicateFunction.body, (child) => {
|
|
75149
|
-
if (didProveNormalizedMatch || isFunctionLike$1(child)) return false;
|
|
75150
|
-
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;
|
|
75151
|
-
const predicateReceiver = stripParenExpression(child.callee.object);
|
|
75152
|
-
if (!isNodeOfType(predicateReceiver, "Identifier")) return;
|
|
75153
|
-
const predicateReceiverSymbol = context.scopes.symbolFor(predicateReceiver);
|
|
75154
|
-
const predicateReceiverInitializer = predicateReceiverSymbol?.initializer ? stripParenExpression(predicateReceiverSymbol.initializer) : null;
|
|
75155
|
-
const predicateRoot = predicateReceiverInitializer ? getNormalizedClassNameRoot(predicateReceiverInitializer) : null;
|
|
75156
|
-
if (predicateReceiverSymbol?.kind === "const" && predicateReceiverSymbol.references.every((reference) => reference.flag === "read") && predicateRoot?.name === predicateParameter.name) {
|
|
75157
|
-
didProveNormalizedMatch = true;
|
|
75158
|
-
return false;
|
|
75159
|
-
}
|
|
75160
|
-
});
|
|
75161
|
-
return didProveNormalizedMatch;
|
|
75162
|
-
};
|
|
75163
73965
|
const scopeProvesFindMatch = (assertion, findReceiver, findPredicate, context) => {
|
|
75164
73966
|
if (!isStablePredicate(findPredicate, context)) return false;
|
|
75165
73967
|
return isPresenceProvenBeforeNode(assertion, (test) => testPositivelyContainsCall(test, (call) => {
|
|
@@ -75260,123 +74062,6 @@ const isEnsureThenFind = (assertion, findReceiver, findPredicate) => {
|
|
|
75260
74062
|
}
|
|
75261
74063
|
return false;
|
|
75262
74064
|
};
|
|
75263
|
-
const getReceiverRootIdentifier = (node) => {
|
|
75264
|
-
let target = stripParenExpression(node);
|
|
75265
|
-
while (isNodeOfType(target, "MemberExpression")) target = stripParenExpression(target.object);
|
|
75266
|
-
return isNodeOfType(target, "Identifier") ? target : null;
|
|
75267
|
-
};
|
|
75268
|
-
const getReceiverStatePath = (node) => {
|
|
75269
|
-
const target = stripParenExpression(node);
|
|
75270
|
-
if (isNodeOfType(target, "Identifier")) return target.name;
|
|
75271
|
-
if (!isNodeOfType(target, "MemberExpression")) return null;
|
|
75272
|
-
const objectPath = getReceiverStatePath(target.object);
|
|
75273
|
-
if (!objectPath) return null;
|
|
75274
|
-
return `${objectPath}.${getStaticPropertyName(target) ?? "*"}`;
|
|
75275
|
-
};
|
|
75276
|
-
const doesReceiverStateChangeBeforeAssertion = (ownerFunction, receiver, startOffset, assertion, context) => {
|
|
75277
|
-
const receiverRoot = getReceiverRootIdentifier(receiver);
|
|
75278
|
-
const receiverSymbol = receiverRoot ? context.scopes.symbolFor(receiverRoot) : null;
|
|
75279
|
-
const receiverPath = getReceiverStatePath(receiver);
|
|
75280
|
-
if (!receiverRoot || !receiverSymbol || !receiverPath) return true;
|
|
75281
|
-
const receiverAliasPaths = new Map([[receiverSymbol.id, receiverRoot.name]]);
|
|
75282
|
-
let didAddAlias = true;
|
|
75283
|
-
while (didAddAlias) {
|
|
75284
|
-
didAddAlias = false;
|
|
75285
|
-
walkAst(ownerFunction, (child) => {
|
|
75286
|
-
if (child !== ownerFunction && isFunctionLike$1(child)) return false;
|
|
75287
|
-
if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier") || !child.init) return;
|
|
75288
|
-
const initializer = stripParenExpression(child.init);
|
|
75289
|
-
if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return;
|
|
75290
|
-
const initializerRoot = getReceiverRootIdentifier(initializer);
|
|
75291
|
-
const initializerSymbol = initializerRoot ? context.scopes.symbolFor(initializerRoot) : null;
|
|
75292
|
-
const initializerBasePath = initializerSymbol ? receiverAliasPaths.get(initializerSymbol.id) : null;
|
|
75293
|
-
const initializerPath = getReceiverStatePath(initializer);
|
|
75294
|
-
if (!initializerRoot || !initializerBasePath || !initializerPath) return;
|
|
75295
|
-
const aliasSymbol = context.scopes.symbolFor(child.id);
|
|
75296
|
-
if (aliasSymbol && !receiverAliasPaths.has(aliasSymbol.id)) {
|
|
75297
|
-
const initializerSuffix = initializerPath.slice(initializerRoot.name.length);
|
|
75298
|
-
receiverAliasPaths.set(aliasSymbol.id, `${initializerBasePath}${initializerSuffix}`);
|
|
75299
|
-
didAddAlias = true;
|
|
75300
|
-
}
|
|
75301
|
-
});
|
|
75302
|
-
}
|
|
75303
|
-
let didChangeReceiverState = false;
|
|
75304
|
-
walkAst(ownerFunction, (child) => {
|
|
75305
|
-
if (didChangeReceiverState) return false;
|
|
75306
|
-
if (child !== ownerFunction && isFunctionLike$1(child)) return false;
|
|
75307
|
-
if (child.range[0] <= startOffset || child.range[0] >= assertion.range[0]) return;
|
|
75308
|
-
if (isNodeOfType(child, "CallExpression")) {
|
|
75309
|
-
didChangeReceiverState = true;
|
|
75310
|
-
return false;
|
|
75311
|
-
}
|
|
75312
|
-
const mutationTarget = isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete" ? stripParenExpression(isNodeOfType(child, "AssignmentExpression") ? child.left : child.argument) : null;
|
|
75313
|
-
const mutationRoot = mutationTarget ? getReceiverRootIdentifier(mutationTarget) : null;
|
|
75314
|
-
const mutationSymbol = mutationRoot ? context.scopes.symbolFor(mutationRoot) : null;
|
|
75315
|
-
const mutationBasePath = mutationSymbol ? receiverAliasPaths.get(mutationSymbol.id) : null;
|
|
75316
|
-
const mutationPath = mutationTarget ? getReceiverStatePath(mutationTarget) : null;
|
|
75317
|
-
if (mutationRoot && mutationBasePath && mutationPath) {
|
|
75318
|
-
const canonicalMutationPath = `${mutationBasePath}${mutationPath.slice(mutationRoot.name.length)}`;
|
|
75319
|
-
if (canonicalMutationPath !== receiverPath && !canonicalMutationPath.startsWith(`${receiverPath}.`) && !receiverPath.startsWith(`${canonicalMutationPath}.`)) return;
|
|
75320
|
-
didChangeReceiverState = true;
|
|
75321
|
-
return false;
|
|
75322
|
-
}
|
|
75323
|
-
});
|
|
75324
|
-
return didChangeReceiverState;
|
|
75325
|
-
};
|
|
75326
|
-
const isFindProvenByGuardedMaximum = (assertion, findReceiver, findPredicate, context) => {
|
|
75327
|
-
const findLookup = findEqualityLookupParts(findPredicate);
|
|
75328
|
-
const maximumIdentifier = findLookup ? stripParenExpression(findLookup.comparedValue) : null;
|
|
75329
|
-
if (!findLookup || !maximumIdentifier || !isNodeOfType(maximumIdentifier, "Identifier")) return false;
|
|
75330
|
-
const maximumSymbol = context.scopes.symbolFor(maximumIdentifier);
|
|
75331
|
-
const maximumInitializer = maximumSymbol?.initializer ? stripParenExpression(maximumSymbol.initializer) : null;
|
|
75332
|
-
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;
|
|
75333
|
-
const filterCall = stripParenExpression(maximumInitializer.callee.object);
|
|
75334
|
-
if (!isNodeOfType(filterCall, "CallExpression") || !isNodeOfType(filterCall.callee, "MemberExpression") || getStaticPropertyName(filterCall.callee) !== "filter" || !areNodesLooselyEqual(filterCall.callee.object, findReceiver)) return false;
|
|
75335
|
-
const filterPredicate = filterCall.arguments[0] ? stripParenExpression(filterCall.arguments[0]) : null;
|
|
75336
|
-
if (!filterPredicate || !isStablePredicate(filterPredicate, context)) return false;
|
|
75337
|
-
const reducerArgument = maximumInitializer.arguments[0] ? stripParenExpression(maximumInitializer.arguments[0]) : null;
|
|
75338
|
-
const reducerFunction = reducerArgument ? resolveExactLocalFunction(reducerArgument, context.scopes) : null;
|
|
75339
|
-
const initialValue = maximumInitializer.arguments[1] ? stripParenExpression(maximumInitializer.arguments[1]) : null;
|
|
75340
|
-
if (!reducerFunction || !isFunctionLike$1(reducerFunction) || reducerFunction.async || reducerFunction.generator || !initialValue || !isNodeOfType(initialValue, "Literal") || typeof initialValue.value !== "number") return false;
|
|
75341
|
-
const accumulatorParameter = reducerFunction.params[0];
|
|
75342
|
-
const itemParameter = reducerFunction.params[1];
|
|
75343
|
-
const reducerBody = singleExpressionPredicateBody(reducerFunction);
|
|
75344
|
-
if (!isNodeOfType(accumulatorParameter, "Identifier") || !isNodeOfType(itemParameter, "Identifier") || !reducerBody || !isNodeOfType(reducerBody, "CallExpression") || !isNodeOfType(reducerBody.callee, "MemberExpression") || getStaticPropertyName(reducerBody.callee) !== "max") return false;
|
|
75345
|
-
const mathReceiver = stripParenExpression(reducerBody.callee.object);
|
|
75346
|
-
if (!isNodeOfType(mathReceiver, "Identifier") || mathReceiver.name !== "Math" || !context.scopes.isGlobalReference(mathReceiver) || reducerBody.arguments.length !== 2) return false;
|
|
75347
|
-
const accumulatorArgument = reducerBody.arguments.find((argument) => {
|
|
75348
|
-
const expression = stripParenExpression(argument);
|
|
75349
|
-
return isNodeOfType(expression, "Identifier") && expression.name === accumulatorParameter.name;
|
|
75350
|
-
});
|
|
75351
|
-
const itemMemberArgument = reducerBody.arguments.find((argument) => {
|
|
75352
|
-
const expression = stripParenExpression(argument);
|
|
75353
|
-
const rootIdentifier = getRootIdentifier(expression);
|
|
75354
|
-
return isNodeOfType(expression, "MemberExpression") && rootIdentifier?.name === itemParameter.name && receiverPathKey(expression)?.slice(itemParameter.name.length + 1) === findLookup.propertyName;
|
|
75355
|
-
});
|
|
75356
|
-
if (!accumulatorArgument || !itemMemberArgument) return false;
|
|
75357
|
-
if (!receiverPathKey(findReceiver)) return false;
|
|
75358
|
-
let ownerFunction = assertion.parent ?? null;
|
|
75359
|
-
while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
|
|
75360
|
-
if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
|
|
75361
|
-
const maximumEnd = maximumInitializer.range[1];
|
|
75362
|
-
if (doesReceiverStateChangeBeforeAssertion(ownerFunction.body, findReceiver, maximumEnd, assertion, context)) return false;
|
|
75363
|
-
return isPresenceProvenBeforeNode(assertion, (test) => {
|
|
75364
|
-
const comparison = stripParenExpression(test);
|
|
75365
|
-
if (!isNodeOfType(comparison, "BinaryExpression")) return false;
|
|
75366
|
-
return [[
|
|
75367
|
-
comparison.left,
|
|
75368
|
-
comparison.right,
|
|
75369
|
-
comparison.operator
|
|
75370
|
-
], [
|
|
75371
|
-
comparison.right,
|
|
75372
|
-
comparison.left,
|
|
75373
|
-
comparison.operator === "<" ? ">" : comparison.operator === ">" ? "<" : comparison.operator
|
|
75374
|
-
]].some(([candidateMaximum, candidateInitial, operator]) => {
|
|
75375
|
-
const candidateMaximumIdentifier = stripParenExpression(candidateMaximum);
|
|
75376
|
-
return operator === ">" && isNodeOfType(candidateMaximumIdentifier, "Identifier") && context.scopes.symbolFor(candidateMaximumIdentifier)?.id === maximumSymbol.id && areNodesLooselyEqual(stripParenExpression(candidateInitial), initialValue);
|
|
75377
|
-
});
|
|
75378
|
-
});
|
|
75379
|
-
};
|
|
75380
74065
|
const isDefinitelyNonNullishMapValue = (value) => {
|
|
75381
74066
|
if (!value) return false;
|
|
75382
74067
|
const expression = stripParenExpression(value);
|
|
@@ -75400,15 +74085,15 @@ const unwrapFalseBooleanGuard = (test) => {
|
|
|
75400
74085
|
};
|
|
75401
74086
|
const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
|
|
75402
74087
|
const stableLookupKey = stripParenExpression(lookupKey);
|
|
75403
|
-
|
|
75404
|
-
const lookupKeySymbol = isNodeOfType(stableLookupKey, "Identifier") ? context.scopes.symbolFor(stableLookupKey) : lookupKeyRoot ? context.scopes.symbolFor(lookupKeyRoot) : null;
|
|
75405
|
-
if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal") && (!isNodeOfType(stableLookupKey, "MemberExpression") || !lookupKeyRoot || lookupKeySymbol?.kind !== "const")) return false;
|
|
74088
|
+
if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal")) return false;
|
|
75406
74089
|
const receiverSymbol = context.scopes.symbolFor(receiver);
|
|
75407
74090
|
if (!receiverSymbol) return false;
|
|
75408
74091
|
const receiverMatches = (candidate) => {
|
|
75409
74092
|
const target = stripParenExpression(candidate);
|
|
75410
74093
|
return isNodeOfType(target, "Identifier") && context.scopes.symbolFor(target)?.id === receiverSymbol.id;
|
|
75411
74094
|
};
|
|
74095
|
+
const lookupKeyExpression = stripParenExpression(lookupKey);
|
|
74096
|
+
const lookupKeySymbol = isNodeOfType(lookupKeyExpression, "Identifier") ? context.scopes.symbolFor(lookupKeyExpression) : null;
|
|
75412
74097
|
let child = assertion;
|
|
75413
74098
|
let ancestor = assertion.parent ?? null;
|
|
75414
74099
|
while (ancestor && !isFunctionLike$1(ancestor)) {
|
|
@@ -75428,7 +74113,7 @@ const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
|
|
|
75428
74113
|
const populationCall = populationCalls[0];
|
|
75429
74114
|
if (!populationCall) continue;
|
|
75430
74115
|
const populationCallStart = populationCall.range[0];
|
|
75431
|
-
if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))
|
|
74116
|
+
if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))) continue;
|
|
75432
74117
|
if (receiverSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0])) continue;
|
|
75433
74118
|
if (!indexedRelevantCalls(ancestor).some((laterCall) => {
|
|
75434
74119
|
if (laterCall.range[0] <= populationCallStart || laterCall.range[0] >= assertion.range[0] || !isNodeOfType(laterCall.callee, "MemberExpression") || !receiverMatches(laterCall.callee.object)) return false;
|
|
@@ -75538,7 +74223,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
|
|
|
75538
74223
|
const findReceiver = callee.object;
|
|
75539
74224
|
if (predicate && isExhaustiveLiteralTupleMapping(findReceiver, predicate, context)) return;
|
|
75540
74225
|
if (predicate && scopeProvesFindMatch(node, findReceiver, predicate, context)) return;
|
|
75541
|
-
if (predicate && isFindProvenByGuardedMaximum(node, findReceiver, predicate, context)) return;
|
|
75542
74226
|
if (predicate && isEnsureThenFind(node, findReceiver, predicate)) return;
|
|
75543
74227
|
}
|
|
75544
74228
|
if (methodName === "match") {
|
|
@@ -75548,7 +74232,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
|
|
|
75548
74232
|
const regexKey = pattern ? regexComparableKey(pattern, context) : null;
|
|
75549
74233
|
if (pattern && isGuardedAnchoredCharacterMatch(node, matchReceiver, pattern)) return;
|
|
75550
74234
|
if (pattern && regexKey && isMatchProvenByFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
|
|
75551
|
-
if (pattern && regexKey && isMatchProvenByNormalizedFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
|
|
75552
74235
|
if (regexKey && scopeProvesMatchTested(node, regexKey, matchReceiver, context)) return;
|
|
75553
74236
|
}
|
|
75554
74237
|
if (methodName === "get") {
|
|
@@ -89688,11 +88371,6 @@ const noUndersizedIconButton = defineRule({
|
|
|
89688
88371
|
//#region src/plugin/rules/correctness/no-unescaped-dynamic-string-in-regexp.ts
|
|
89689
88372
|
const TEST_CONTEXT_FILE_PATTERN = /(\.test\.|\.spec\.|__tests__|(^|\/)(test|tests|e2e|cypress|playwright)\/)/;
|
|
89690
88373
|
const SEARCH_TERM_NAME_PATTERN = /search|query|highlight|filter|term(?!in)|keyword/i;
|
|
89691
|
-
const REGEX_SOURCE_WORDS = [
|
|
89692
|
-
"pattern",
|
|
89693
|
-
"regex",
|
|
89694
|
-
"regexp"
|
|
89695
|
-
];
|
|
89696
88374
|
const ESCAPE_HELPER_NAME_PATTERN = /escape.*reg|safe.*reg/i;
|
|
89697
88375
|
const SANITIZED_NAME_PATTERN = /escap|sanitiz/i;
|
|
89698
88376
|
const INITIALIZER_RESOLUTION_HOPS = 2;
|
|
@@ -89846,70 +88524,14 @@ const isTypePositionIdentifier = (identifier) => {
|
|
|
89846
88524
|
}
|
|
89847
88525
|
return false;
|
|
89848
88526
|
};
|
|
89849
|
-
const
|
|
89850
|
-
const
|
|
89851
|
-
if (identifierWords.some((word) => REGEX_SOURCE_WORDS.includes(word))) return false;
|
|
89852
|
-
return identifierWords.some((word) => [
|
|
89853
|
-
"path",
|
|
89854
|
-
"folder",
|
|
89855
|
-
"directory",
|
|
89856
|
-
"root"
|
|
89857
|
-
].includes(word)) || identifierWords.includes("top") && identifierWords.includes("level");
|
|
89858
|
-
};
|
|
89859
|
-
const flattenPatternParts = (expression) => {
|
|
89860
|
-
const inner = stripParenExpression(expression);
|
|
89861
|
-
const staticValue = literalStringValue(inner);
|
|
89862
|
-
if (staticValue !== null) return [staticValue];
|
|
89863
|
-
if (isNodeOfType(inner, "Identifier")) return [inner];
|
|
89864
|
-
if (isNodeOfType(inner, "TemplateLiteral")) {
|
|
89865
|
-
const parts = [];
|
|
89866
|
-
for (let index = 0; index < inner.quasis.length; index += 1) {
|
|
89867
|
-
const quasi = inner.quasis[index];
|
|
89868
|
-
if (quasi) parts.push(quasi.value.cooked ?? quasi.value.raw);
|
|
89869
|
-
const templateExpression = inner.expressions[index];
|
|
89870
|
-
if (templateExpression) parts.push(stripParenExpression(templateExpression));
|
|
89871
|
-
}
|
|
89872
|
-
return parts;
|
|
89873
|
-
}
|
|
89874
|
-
if (isNodeOfType(inner, "BinaryExpression") && inner.operator === "+") {
|
|
89875
|
-
const leftParts = flattenPatternParts(inner.left);
|
|
89876
|
-
const rightParts = flattenPatternParts(inner.right);
|
|
89877
|
-
return leftParts && rightParts ? [...leftParts, ...rightParts] : null;
|
|
89878
|
-
}
|
|
89879
|
-
if (isNodeOfType(inner, "CallExpression") && isNodeOfType(inner.callee, "MemberExpression") && getStaticPropertyName(inner.callee) === "concat") {
|
|
89880
|
-
const receiverParts = flattenPatternParts(inner.callee.object);
|
|
89881
|
-
if (!receiverParts) return null;
|
|
89882
|
-
const parts = [...receiverParts];
|
|
89883
|
-
for (const argument of inner.arguments) {
|
|
89884
|
-
if (isNodeOfType(argument, "SpreadElement")) return null;
|
|
89885
|
-
const argumentParts = flattenPatternParts(argument);
|
|
89886
|
-
if (!argumentParts) return null;
|
|
89887
|
-
parts.push(...argumentParts);
|
|
89888
|
-
}
|
|
89889
|
-
return parts;
|
|
89890
|
-
}
|
|
89891
|
-
return null;
|
|
89892
|
-
};
|
|
89893
|
-
const isIdentifierAnAnchoredPathSegment = (argument, identifier) => {
|
|
89894
|
-
if (!identifierNameHasPathSegmentSemantics(identifier.name)) return false;
|
|
89895
|
-
const parts = flattenPatternParts(argument);
|
|
89896
|
-
if (!parts) return false;
|
|
89897
|
-
const identifierPartIndex = parts.findIndex((part) => part === identifier);
|
|
89898
|
-
if (identifierPartIndex < 0) return false;
|
|
89899
|
-
const precedingParts = parts.slice(0, identifierPartIndex);
|
|
89900
|
-
if (precedingParts.some((part) => typeof part !== "string")) return false;
|
|
89901
|
-
const staticPrefix = precedingParts.join("");
|
|
89902
|
-
const followingPart = parts[identifierPartIndex + 1];
|
|
89903
|
-
return staticPrefix === "^" && typeof followingPart === "string" && followingPart.startsWith("/");
|
|
89904
|
-
};
|
|
89905
|
-
const collectRawDynamicLiteralIdentifiers = (argument) => {
|
|
89906
|
-
const rawDynamicLiteralIdentifiers = [];
|
|
88527
|
+
const collectRawSearchTermIdentifiers = (argument) => {
|
|
88528
|
+
const rawSearchTermIdentifiers = [];
|
|
89907
88529
|
walkAst(argument, (child) => {
|
|
89908
88530
|
if (isEscapingCall(child) || isRegexSourceAccess(child)) return false;
|
|
89909
88531
|
if (isLiteralReturningGetterCall(child)) return false;
|
|
89910
|
-
if (isNodeOfType(child, "Identifier") &&
|
|
88532
|
+
if (isNodeOfType(child, "Identifier") && SEARCH_TERM_NAME_PATTERN.test(child.name) && !isPropertyNamePosition(child) && !isTypePositionIdentifier(child)) rawSearchTermIdentifiers.push(child);
|
|
89911
88533
|
});
|
|
89912
|
-
return
|
|
88534
|
+
return rawSearchTermIdentifiers;
|
|
89913
88535
|
};
|
|
89914
88536
|
const collectLeafIdentifiers = (node) => {
|
|
89915
88537
|
const leafIdentifiers = [];
|
|
@@ -89920,11 +88542,8 @@ const collectLeafIdentifiers = (node) => {
|
|
|
89920
88542
|
return leafIdentifiers;
|
|
89921
88543
|
};
|
|
89922
88544
|
const compositeInitializerResolvesEscaped = (strippedInitializer, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames) => {
|
|
89923
|
-
if (isNodeOfType(strippedInitializer, "ConditionalExpression")) return [strippedInitializer.consequent, strippedInitializer.alternate].every((branch) => initializerLooksEscaped(branch, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames));
|
|
89924
|
-
if (isNodeOfType(strippedInitializer, "BinaryExpression") || isNodeOfType(strippedInitializer, "LogicalExpression")) return [strippedInitializer.left, strippedInitializer.right].every((operand) => initializerLooksEscaped(operand, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames));
|
|
89925
|
-
if (isNodeOfType(strippedInitializer, "TemplateLiteral")) return strippedInitializer.expressions.every((expression) => initializerLooksEscaped(expression, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames));
|
|
89926
88545
|
let didResolveAnyLeafEscaped = false;
|
|
89927
|
-
for (const leafIdentifier of collectLeafIdentifiers(strippedInitializer)) if (identifierResolvesToEscapedValue(leafIdentifier, remainingHops
|
|
88546
|
+
for (const leafIdentifier of collectLeafIdentifiers(strippedInitializer)) if (identifierResolvesToEscapedValue(leafIdentifier, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames)) didResolveAnyLeafEscaped = true;
|
|
89928
88547
|
else if (SEARCH_TERM_NAME_PATTERN.test(leafIdentifier.name)) return false;
|
|
89929
88548
|
return didResolveAnyLeafEscaped;
|
|
89930
88549
|
};
|
|
@@ -89936,7 +88555,7 @@ const initializerLooksEscaped = (initializer, remainingHops, scopes, regexpObjec
|
|
|
89936
88555
|
if (isNodeOfType(strippedInitializer, "CallExpression") && (isEscapingCall(strippedInitializer) || calleeBindingBodyEscapes(strippedInitializer))) return true;
|
|
89937
88556
|
if (remainingHops > 0) {
|
|
89938
88557
|
if (isNodeOfType(strippedInitializer, "Identifier")) return identifierResolvesToEscapedValue(strippedInitializer, remainingHops - 1, scopes, regexpObjectSymbolIds, globalRegExpObjectNames);
|
|
89939
|
-
return compositeInitializerResolvesEscaped(strippedInitializer, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames);
|
|
88558
|
+
return compositeInitializerResolvesEscaped(strippedInitializer, remainingHops - 1, scopes, regexpObjectSymbolIds, globalRegExpObjectNames);
|
|
89940
88559
|
}
|
|
89941
88560
|
return false;
|
|
89942
88561
|
};
|
|
@@ -90128,7 +88747,7 @@ const noUnescapedDynamicStringInRegexp = defineRule({
|
|
|
90128
88747
|
severity: "warn",
|
|
90129
88748
|
category: "Correctness",
|
|
90130
88749
|
tags: ["test-noise"],
|
|
90131
|
-
recommendation: "A
|
|
88750
|
+
recommendation: "A search/filter/highlight term dropped straight into `new RegExp(...)` lets its regex metacharacters act as operators, so a user typing `.` or `(` over-matches or throws. Escape the value with an `escapeRegExp` helper before constructing the pattern.",
|
|
90132
88751
|
create: (context) => {
|
|
90133
88752
|
if (TEST_CONTEXT_FILE_PATTERN.test(context.filename ?? "")) return {};
|
|
90134
88753
|
let regexpObjectIndex = null;
|
|
@@ -90145,10 +88764,10 @@ const noUnescapedDynamicStringInRegexp = defineRule({
|
|
|
90145
88764
|
regexpObjectIndex = buildRegExpObjectIndex(programRoot, context.scopes);
|
|
90146
88765
|
}
|
|
90147
88766
|
const currentRegExpObjectIndex = regexpObjectIndex;
|
|
90148
|
-
if (!
|
|
88767
|
+
if (!collectRawSearchTermIdentifiers(firstArgument).some((identifier) => !identifierResolvesToEscapedValue(identifier, INITIALIZER_RESOLUTION_HOPS, context.scopes, currentRegExpObjectIndex.regexpObjectSymbolIds, currentRegExpObjectIndex.globalRegExpObjectNames) && !isShapeTestedByDominatingGuard(node, identifier, context.scopes) && !isParameterFedOnlyMetacharacterFreeLiterals(identifier, context.scopes))) return;
|
|
90149
88768
|
context.report({
|
|
90150
88769
|
node,
|
|
90151
|
-
message: "This builds a `RegExp` from a dynamic
|
|
88770
|
+
message: "This builds a `RegExp` from a dynamic search/filter term without escaping it, so regex metacharacters in the value act as operators and over-match or throw. Escape the value with an `escapeRegExp` helper first."
|
|
90152
88771
|
});
|
|
90153
88772
|
};
|
|
90154
88773
|
return {
|
|
@@ -109890,6 +108509,9 @@ const rerenderFunctionalSetstate = defineRule({
|
|
|
109890
108509
|
} })
|
|
109891
108510
|
});
|
|
109892
108511
|
//#endregion
|
|
108512
|
+
//#region src/plugin/utils/is-trivial-built-in-construction.ts
|
|
108513
|
+
const isTrivialBuiltInConstruction = (expression) => isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && TRIVIAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && (expression.arguments ?? []).length === 0;
|
|
108514
|
+
//#endregion
|
|
109893
108515
|
//#region src/plugin/rules/state-and-effects/rerender-lazy-ref-init.ts
|
|
109894
108516
|
const rerenderLazyRefInit = defineRule({
|
|
109895
108517
|
id: "rerender-lazy-ref-init",
|
|
@@ -109908,6 +108530,7 @@ const rerenderLazyRefInit = defineRule({
|
|
|
109908
108530
|
const memberPropertyName = isNodeOfType(callee, "MemberExpression") && (isNodeOfType(callee.property, "Identifier") || isNodeOfType(callee.property, "PrivateIdentifier")) ? callee.property.name : null;
|
|
109909
108531
|
const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : memberPropertyName ?? "fn";
|
|
109910
108532
|
if (TRIVIAL_INITIALIZER_NAMES.has(calleeName)) return;
|
|
108533
|
+
if (isTrivialBuiltInConstruction(initializer)) return;
|
|
109911
108534
|
if (isPlainCall && isReactHookName(calleeName)) return;
|
|
109912
108535
|
const callShape = isNewCall ? `new ${calleeName}()` : `${calleeName}()`;
|
|
109913
108536
|
context.report({
|