oxlint-plugin-react-doctor 0.8.1-dev.b852961 → 0.8.1-dev.c9f2c4b
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.d.ts +2 -2
- package/dist/index.js +230 -2080
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -762,7 +762,7 @@ const EXTERNAL_SYNC_MEMBER_METHOD_NAMES = new Set([
|
|
|
762
762
|
"put",
|
|
763
763
|
"patch"
|
|
764
764
|
]);
|
|
765
|
-
new Set([
|
|
765
|
+
const EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS = new Set([
|
|
766
766
|
...FETCH_MEMBER_OBJECTS,
|
|
767
767
|
"api",
|
|
768
768
|
"client",
|
|
@@ -9008,16 +9008,6 @@ const hasVisibleBindingNamed = (node, bindingName, scopes) => {
|
|
|
9008
9008
|
}
|
|
9009
9009
|
};
|
|
9010
9010
|
//#endregion
|
|
9011
|
-
//#region src/plugin/utils/is-global-match-media-call.ts
|
|
9012
|
-
const isGlobalMatchMediaCall = (node, scopes) => {
|
|
9013
|
-
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
9014
|
-
const callee = stripParenExpression(node.callee);
|
|
9015
|
-
if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
|
|
9016
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
|
|
9017
|
-
const receiver = stripParenExpression(callee.object);
|
|
9018
|
-
return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
|
|
9019
|
-
};
|
|
9020
|
-
//#endregion
|
|
9021
9011
|
//#region src/plugin/utils/is-proven-browser-api-receiver.ts
|
|
9022
9012
|
const DOM_EVENT_TARGET_TYPE_NAMES = new Set([
|
|
9023
9013
|
"AbortSignal",
|
|
@@ -9164,10 +9154,8 @@ const getProvenDomEventTargetPrototypeOwnerNames = (rawExpression, scopes, visit
|
|
|
9164
9154
|
if (memberName === "body" || memberName === "documentElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
|
|
9165
9155
|
}
|
|
9166
9156
|
if (isNodeOfType(expression, "CallExpression")) {
|
|
9167
|
-
if (isGlobalMatchMediaCall(expression, scopes)) return getDomPrototypeOwnerNamesForType("MediaQueryList");
|
|
9168
9157
|
const callee = stripParenExpression(expression.callee);
|
|
9169
9158
|
const methodName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : null;
|
|
9170
|
-
if (methodName === "matchMedia" && isNodeOfType(callee, "MemberExpression") && getProvenDomEventTargetPrototypeOwnerNames(callee.object, scopes, visitedSymbolIds).includes("Window")) return getDomPrototypeOwnerNamesForType("MediaQueryList");
|
|
9171
9159
|
if (methodName === "createElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
|
|
9172
9160
|
if (methodName === "createElementNS") return getDomPrototypeOwnerNamesForType("SVGElement");
|
|
9173
9161
|
if (methodName && DOM_EVENT_TARGET_FACTORY_METHOD_NAMES.has(methodName)) return getDomPrototypeOwnerNamesForType("Element");
|
|
@@ -13827,116 +13815,6 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
|
|
|
13827
13815
|
return null;
|
|
13828
13816
|
};
|
|
13829
13817
|
//#endregion
|
|
13830
|
-
//#region src/plugin/utils/get-final-sequence-expression-value.ts
|
|
13831
|
-
const getFinalSequenceExpressionValue = (expression) => {
|
|
13832
|
-
let finalExpression = stripParenExpression(expression);
|
|
13833
|
-
while (isNodeOfType(finalExpression, "SequenceExpression")) {
|
|
13834
|
-
const sequenceResult = finalExpression.expressions.at(-1);
|
|
13835
|
-
if (!sequenceResult) break;
|
|
13836
|
-
finalExpression = stripParenExpression(sequenceResult);
|
|
13837
|
-
}
|
|
13838
|
-
return finalExpression;
|
|
13839
|
-
};
|
|
13840
|
-
//#endregion
|
|
13841
|
-
//#region src/plugin/utils/collect-expression-path-coverage-nodes.ts
|
|
13842
|
-
const collectExpressionPathCoverageNodes = (owner, matchingNodes, context, expressionBoundary = owner) => {
|
|
13843
|
-
const coverageNodes = /* @__PURE__ */ new Set();
|
|
13844
|
-
const pendingNodes = matchingNodes.filter((matchingNode) => {
|
|
13845
|
-
if (context.cfg.enclosingFunction(matchingNode) !== owner) return false;
|
|
13846
|
-
let currentNode = matchingNode;
|
|
13847
|
-
while (currentNode && currentNode !== expressionBoundary) currentNode = currentNode.parent ?? null;
|
|
13848
|
-
return currentNode === expressionBoundary;
|
|
13849
|
-
});
|
|
13850
|
-
const visitedNodes = new Set(pendingNodes);
|
|
13851
|
-
const coveredBranchesByConditional = /* @__PURE__ */ new Map();
|
|
13852
|
-
while (pendingNodes.length > 0) {
|
|
13853
|
-
const coverageCandidate = pendingNodes.pop();
|
|
13854
|
-
if (!coverageCandidate) break;
|
|
13855
|
-
if (coverageCandidate === expressionBoundary) {
|
|
13856
|
-
coverageNodes.add(coverageCandidate);
|
|
13857
|
-
continue;
|
|
13858
|
-
}
|
|
13859
|
-
let currentChild = coverageCandidate;
|
|
13860
|
-
let currentParent = currentChild.parent ?? null;
|
|
13861
|
-
let conditionalExpression = null;
|
|
13862
|
-
let conditionalBranch = null;
|
|
13863
|
-
let isBlockedByNonExhaustiveExpression = false;
|
|
13864
|
-
while (currentParent && currentParent !== expressionBoundary) {
|
|
13865
|
-
if (isNodeOfType(currentParent, "ConditionalExpression")) {
|
|
13866
|
-
const isConsequent = currentParent.consequent === currentChild;
|
|
13867
|
-
const isAlternate = currentParent.alternate === currentChild;
|
|
13868
|
-
if (isConsequent || isAlternate) {
|
|
13869
|
-
const staticTestValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.test));
|
|
13870
|
-
if (staticTestValue !== null) {
|
|
13871
|
-
if (staticTestValue !== isConsequent) {
|
|
13872
|
-
isBlockedByNonExhaustiveExpression = true;
|
|
13873
|
-
break;
|
|
13874
|
-
}
|
|
13875
|
-
currentChild = currentParent;
|
|
13876
|
-
currentParent = currentChild.parent ?? null;
|
|
13877
|
-
continue;
|
|
13878
|
-
}
|
|
13879
|
-
conditionalExpression = currentParent;
|
|
13880
|
-
conditionalBranch = isConsequent ? "consequent" : "alternate";
|
|
13881
|
-
break;
|
|
13882
|
-
}
|
|
13883
|
-
}
|
|
13884
|
-
if (isNodeOfType(currentParent, "LogicalExpression") && currentParent.right === currentChild) {
|
|
13885
|
-
const staticLeftValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.left));
|
|
13886
|
-
if (!(currentParent.operator === "&&" && staticLeftValue === true || currentParent.operator === "||" && staticLeftValue === false)) {
|
|
13887
|
-
isBlockedByNonExhaustiveExpression = true;
|
|
13888
|
-
break;
|
|
13889
|
-
}
|
|
13890
|
-
}
|
|
13891
|
-
if (isNodeOfType(currentParent, "AssignmentPattern") && currentParent.right === currentChild) {
|
|
13892
|
-
isBlockedByNonExhaustiveExpression = true;
|
|
13893
|
-
break;
|
|
13894
|
-
}
|
|
13895
|
-
currentChild = currentParent;
|
|
13896
|
-
currentParent = currentChild.parent ?? null;
|
|
13897
|
-
}
|
|
13898
|
-
if (isBlockedByNonExhaustiveExpression) continue;
|
|
13899
|
-
if (!conditionalExpression || !conditionalBranch) {
|
|
13900
|
-
coverageNodes.add(coverageCandidate);
|
|
13901
|
-
continue;
|
|
13902
|
-
}
|
|
13903
|
-
const coveredBranches = coveredBranchesByConditional.get(conditionalExpression) ?? /* @__PURE__ */ new Set();
|
|
13904
|
-
coveredBranches.add(conditionalBranch);
|
|
13905
|
-
coveredBranchesByConditional.set(conditionalExpression, coveredBranches);
|
|
13906
|
-
if (coveredBranches.has("alternate") && coveredBranches.has("consequent") && !visitedNodes.has(conditionalExpression)) {
|
|
13907
|
-
visitedNodes.add(conditionalExpression);
|
|
13908
|
-
pendingNodes.push(conditionalExpression);
|
|
13909
|
-
}
|
|
13910
|
-
}
|
|
13911
|
-
return coverageNodes;
|
|
13912
|
-
};
|
|
13913
|
-
//#endregion
|
|
13914
|
-
//#region src/plugin/utils/do-nodes-cover-every-path-from-function-entry.ts
|
|
13915
|
-
const doNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context, options = {}) => {
|
|
13916
|
-
const functionCfg = context.cfg.cfgFor(owner);
|
|
13917
|
-
if (!functionCfg) return false;
|
|
13918
|
-
const matchingBlocks = new Set([...collectExpressionPathCoverageNodes(owner, matchingNodes, context)].flatMap((matchingNode) => {
|
|
13919
|
-
const matchingBlock = functionCfg.blockOf(matchingNode);
|
|
13920
|
-
return matchingBlock ? [matchingBlock] : [];
|
|
13921
|
-
}));
|
|
13922
|
-
if (matchingBlocks.size === 0) return false;
|
|
13923
|
-
const visitedBlocks = new Set([functionCfg.entry]);
|
|
13924
|
-
const pendingBlocks = [functionCfg.entry];
|
|
13925
|
-
while (pendingBlocks.length > 0) {
|
|
13926
|
-
const currentBlock = pendingBlocks.pop();
|
|
13927
|
-
if (!currentBlock) break;
|
|
13928
|
-
if (matchingBlocks.has(currentBlock)) continue;
|
|
13929
|
-
for (const edge of currentBlock.successors) {
|
|
13930
|
-
if (options.ignoreThrowEdges && edge.kind === "throw") continue;
|
|
13931
|
-
if (edge.to === functionCfg.exit) return false;
|
|
13932
|
-
if (visitedBlocks.has(edge.to)) continue;
|
|
13933
|
-
visitedBlocks.add(edge.to);
|
|
13934
|
-
pendingBlocks.push(edge.to);
|
|
13935
|
-
}
|
|
13936
|
-
}
|
|
13937
|
-
return true;
|
|
13938
|
-
};
|
|
13939
|
-
//#endregion
|
|
13940
13818
|
//#region src/plugin/utils/get-function-binding-name.ts
|
|
13941
13819
|
const getFunctionBindingIdentifier$1 = (functionNode) => {
|
|
13942
13820
|
if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
|
|
@@ -13974,19 +13852,14 @@ const isAstDescendant = (inner, outer) => {
|
|
|
13974
13852
|
};
|
|
13975
13853
|
//#endregion
|
|
13976
13854
|
//#region src/plugin/utils/react-ref-origin.ts
|
|
13977
|
-
const
|
|
13978
|
-
const resolveReactRefSymbol = (memberExpression, scopes, options = {}) => {
|
|
13855
|
+
const resolveReactRefSymbol = (memberExpression, scopes) => {
|
|
13979
13856
|
const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
|
|
13980
13857
|
if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticPropertyName(memberExpression) !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
|
|
13981
13858
|
const symbol = resolveConstIdentifierAlias(receiver, scopes);
|
|
13982
13859
|
if (!symbol?.initializer) return null;
|
|
13983
13860
|
const initializer = stripParenExpression(symbol.initializer);
|
|
13984
13861
|
if (!isNodeOfType(initializer, "CallExpression")) return null;
|
|
13985
|
-
return isReactApiCall(initializer,
|
|
13986
|
-
allowGlobalReactNamespace: true,
|
|
13987
|
-
allowUnboundBareCalls: options.allowUnboundBareCalls,
|
|
13988
|
-
resolveNamedAliases: options.resolveNamedAliases
|
|
13989
|
-
}) ? symbol : null;
|
|
13862
|
+
return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
|
|
13990
13863
|
};
|
|
13991
13864
|
const resolveReactRefCurrentOriginSymbol = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
13992
13865
|
const expression = stripParenExpression(node);
|
|
@@ -14085,35 +13958,9 @@ const isNodeReachableWithinFunction = (node, context) => {
|
|
|
14085
13958
|
return false;
|
|
14086
13959
|
};
|
|
14087
13960
|
//#endregion
|
|
14088
|
-
//#region src/plugin/utils/is-within-assignment-target.ts
|
|
14089
|
-
const isWithinAssignmentTarget = (identifier) => {
|
|
14090
|
-
let currentNode = identifier;
|
|
14091
|
-
let parentNode = currentNode.parent;
|
|
14092
|
-
while (parentNode) {
|
|
14093
|
-
if (isNodeOfType(parentNode, "AssignmentExpression")) return parentNode.left === currentNode;
|
|
14094
|
-
if (isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete") return parentNode.argument === currentNode;
|
|
14095
|
-
if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.left === currentNode;
|
|
14096
|
-
currentNode = parentNode;
|
|
14097
|
-
parentNode = currentNode.parent;
|
|
14098
|
-
}
|
|
14099
|
-
return false;
|
|
14100
|
-
};
|
|
14101
|
-
//#endregion
|
|
14102
13961
|
//#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
|
|
14103
13962
|
const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
|
|
14104
13963
|
const REPLAYABLE_ITERATOR_COLLECTION_CACHE = /* @__PURE__ */ new WeakMap();
|
|
14105
|
-
const REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES = new Set([
|
|
14106
|
-
"pop",
|
|
14107
|
-
"shift",
|
|
14108
|
-
"splice",
|
|
14109
|
-
"fill",
|
|
14110
|
-
"copyWithin"
|
|
14111
|
-
]);
|
|
14112
|
-
const REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES = new Set([
|
|
14113
|
-
"clear",
|
|
14114
|
-
"delete",
|
|
14115
|
-
"set"
|
|
14116
|
-
]);
|
|
14117
13964
|
const REACT_REF_EFFECT_ANALYSIS_CACHE = /* @__PURE__ */ new WeakMap();
|
|
14118
13965
|
const RESOURCE_NOUN_BY_KIND = {
|
|
14119
13966
|
subscribe: "subscription",
|
|
@@ -14154,92 +14001,6 @@ const resolveExpressionKey = (expression, context, visitedSymbolIds = /* @__PURE
|
|
|
14154
14001
|
}
|
|
14155
14002
|
return null;
|
|
14156
14003
|
};
|
|
14157
|
-
const resolveForEachProjection = (expression, context) => {
|
|
14158
|
-
if (!expression) return null;
|
|
14159
|
-
let currentExpression = stripParenExpression(expression);
|
|
14160
|
-
const memberNames = [];
|
|
14161
|
-
while (isNodeOfType(currentExpression, "MemberExpression") && !currentExpression.computed) {
|
|
14162
|
-
if (!isNodeOfType(currentExpression.property, "Identifier")) return null;
|
|
14163
|
-
memberNames.unshift(currentExpression.property.name);
|
|
14164
|
-
currentExpression = stripParenExpression(currentExpression.object);
|
|
14165
|
-
}
|
|
14166
|
-
if (!isNodeOfType(currentExpression, "Identifier")) return null;
|
|
14167
|
-
const symbol = context.scopes.symbolFor(currentExpression);
|
|
14168
|
-
if (!symbol || symbol.kind !== "parameter") return null;
|
|
14169
|
-
let callbackNode = symbol.bindingIdentifier.parent;
|
|
14170
|
-
while (callbackNode && !isFunctionLike$1(callbackNode)) callbackNode = callbackNode.parent;
|
|
14171
|
-
if (!callbackNode || !isFunctionLike$1(callbackNode)) return null;
|
|
14172
|
-
const forEachCall = findEnclosingForEachCall(callbackNode);
|
|
14173
|
-
if (!forEachCall) return null;
|
|
14174
|
-
const forEachCallee = stripParenExpression(forEachCall.callee);
|
|
14175
|
-
if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
|
|
14176
|
-
const collectionKey = resolveExpressionKey(forEachCallee.object, context);
|
|
14177
|
-
if (!collectionKey) return null;
|
|
14178
|
-
const firstParameter = callbackNode.params[0];
|
|
14179
|
-
const bindingProperty = symbol.bindingIdentifier.parent;
|
|
14180
|
-
const propertyName = isNodeOfType(firstParameter, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.parent === firstParameter && bindingProperty.value === symbol.bindingIdentifier ? getStaticPropertyKeyName(bindingProperty) : null;
|
|
14181
|
-
const parameterProjection = firstParameter === symbol.bindingIdentifier ? "value" : propertyName;
|
|
14182
|
-
if (!parameterProjection) return null;
|
|
14183
|
-
return {
|
|
14184
|
-
collectionKey,
|
|
14185
|
-
projectionKey: [parameterProjection, ...memberNames].join(".")
|
|
14186
|
-
};
|
|
14187
|
-
};
|
|
14188
|
-
const resolveForEachProjectionKey = (expression, context) => {
|
|
14189
|
-
const projection = resolveForEachProjection(expression, context);
|
|
14190
|
-
return projection ? `forEach:${projection.collectionKey}:${projection.projectionKey}` : null;
|
|
14191
|
-
};
|
|
14192
|
-
const resolveResourceIdentityKey = (expression, context) => resolveForEachProjectionKey(expression, context) ?? resolveExpressionKey(expression, context);
|
|
14193
|
-
const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
|
|
14194
|
-
const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
|
|
14195
|
-
if (capture !== null) return `capture:${String(capture)}`;
|
|
14196
|
-
if (!optionsNode) return null;
|
|
14197
|
-
const unwrappedOptions = stripParenExpression(optionsNode);
|
|
14198
|
-
if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
|
|
14199
|
-
const optionsKey = allowOpaqueOptionsIdentity ? resolveResourceIdentityKey(unwrappedOptions, context) : null;
|
|
14200
|
-
return optionsKey ? `options:${optionsKey}` : null;
|
|
14201
|
-
}
|
|
14202
|
-
let captureKey = "capture:false";
|
|
14203
|
-
for (const property of unwrappedOptions.properties ?? []) {
|
|
14204
|
-
if (!isNodeOfType(property, "Property")) {
|
|
14205
|
-
captureKey = null;
|
|
14206
|
-
continue;
|
|
14207
|
-
}
|
|
14208
|
-
const propertyName = getStaticPropertyKeyName(property);
|
|
14209
|
-
if (propertyName === null || !property.computed && propertyName === "__proto__") {
|
|
14210
|
-
captureKey = null;
|
|
14211
|
-
continue;
|
|
14212
|
-
}
|
|
14213
|
-
if (propertyName === "capture") {
|
|
14214
|
-
const propertyValueKey = resolveResourceIdentityKey(property.value, context);
|
|
14215
|
-
captureKey = propertyValueKey ? `capture-value:${propertyValueKey}` : null;
|
|
14216
|
-
}
|
|
14217
|
-
}
|
|
14218
|
-
return captureKey;
|
|
14219
|
-
};
|
|
14220
|
-
const resolveEventListenerCaptureProjection = (optionsNode, context) => {
|
|
14221
|
-
if (!optionsNode) return null;
|
|
14222
|
-
const unwrappedOptions = stripParenExpression(optionsNode);
|
|
14223
|
-
if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return resolveForEachProjection(unwrappedOptions, context);
|
|
14224
|
-
let captureProjection = null;
|
|
14225
|
-
for (const property of unwrappedOptions.properties ?? []) {
|
|
14226
|
-
if (!isNodeOfType(property, "Property")) {
|
|
14227
|
-
captureProjection = null;
|
|
14228
|
-
continue;
|
|
14229
|
-
}
|
|
14230
|
-
const propertyName = getStaticPropertyKeyName(property);
|
|
14231
|
-
if (propertyName === null || !property.computed && propertyName === "__proto__") {
|
|
14232
|
-
captureProjection = null;
|
|
14233
|
-
continue;
|
|
14234
|
-
}
|
|
14235
|
-
if (propertyName === "capture") captureProjection = resolveForEachProjection(property.value, context);
|
|
14236
|
-
}
|
|
14237
|
-
return captureProjection;
|
|
14238
|
-
};
|
|
14239
|
-
const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, context, allowOpaqueOptionsIdentity = false) => {
|
|
14240
|
-
const registrationCaptureKey = resolveEventListenerCaptureIdentityKey(registrationOptions, context, allowOpaqueOptionsIdentity);
|
|
14241
|
-
return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
|
|
14242
|
-
};
|
|
14243
14004
|
const findAssignedResourceKey = (resourceNode, context) => {
|
|
14244
14005
|
let currentNode = resourceNode;
|
|
14245
14006
|
let parentNode = currentNode.parent;
|
|
@@ -14251,30 +14012,6 @@ const findAssignedResourceKey = (resourceNode, context) => {
|
|
|
14251
14012
|
if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
|
|
14252
14013
|
return null;
|
|
14253
14014
|
};
|
|
14254
|
-
const resolveStableMediaQueryListenerIdentityKey = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14255
|
-
if (!expression) return null;
|
|
14256
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
14257
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
14258
|
-
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
14259
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier))) return null;
|
|
14260
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
14261
|
-
if (!initializer) return `symbol:${symbol.id}`;
|
|
14262
|
-
const unwrappedInitializer = stripParenExpression(initializer);
|
|
14263
|
-
if (!isNodeOfType(unwrappedInitializer, "Identifier")) return `symbol:${symbol.id}`;
|
|
14264
|
-
const nextVisitedSymbolIds = new Set(visitedSymbolIds);
|
|
14265
|
-
nextVisitedSymbolIds.add(symbol.id);
|
|
14266
|
-
return resolveStableMediaQueryListenerIdentityKey(unwrappedInitializer, context, nextVisitedSymbolIds) ?? `symbol:${symbol.id}`;
|
|
14267
|
-
}
|
|
14268
|
-
if (isFunctionLike$1(unwrappedExpression)) {
|
|
14269
|
-
const rangeStart = getRangeStart(unwrappedExpression);
|
|
14270
|
-
return rangeStart === null ? null : `function:${rangeStart}`;
|
|
14271
|
-
}
|
|
14272
|
-
return null;
|
|
14273
|
-
};
|
|
14274
|
-
const isProvenLegacyMediaQueryListMethodCall = (callNode, methodName, context) => {
|
|
14275
|
-
const callee = stripParenExpression(callNode.callee);
|
|
14276
|
-
return callNode.arguments?.length === 1 && isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName && getProvenDomEventTargetPrototypeOwnerNames(callee.object, context.scopes).includes("MediaQueryList");
|
|
14277
|
-
};
|
|
14278
14015
|
const getCallRegistrationDetails = (callNode, context) => {
|
|
14279
14016
|
const callee = stripParenExpression(callNode.callee);
|
|
14280
14017
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return {
|
|
@@ -14283,17 +14020,11 @@ const getCallRegistrationDetails = (callNode, context) => {
|
|
|
14283
14020
|
eventKey: null,
|
|
14284
14021
|
handlerKey: null
|
|
14285
14022
|
};
|
|
14286
|
-
if (isProvenLegacyMediaQueryListMethodCall(callNode, "addListener", context)) return {
|
|
14287
|
-
receiverKey: resolveStableMediaQueryListenerIdentityKey(callee.object, context),
|
|
14288
|
-
registrationVerbName: callee.property.name,
|
|
14289
|
-
eventKey: null,
|
|
14290
|
-
handlerKey: resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context)
|
|
14291
|
-
};
|
|
14292
14023
|
return {
|
|
14293
|
-
receiverKey:
|
|
14024
|
+
receiverKey: resolveExpressionKey(callee.object, context),
|
|
14294
14025
|
registrationVerbName: callee.property.name,
|
|
14295
|
-
eventKey:
|
|
14296
|
-
handlerKey:
|
|
14026
|
+
eventKey: resolveExpressionKey(callNode.arguments?.[0], context),
|
|
14027
|
+
handlerKey: resolveExpressionKey(callNode.arguments?.[1], context)
|
|
14297
14028
|
};
|
|
14298
14029
|
};
|
|
14299
14030
|
const findSubscribeLikeUsages = (callback, context) => {
|
|
@@ -14393,6 +14124,30 @@ const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, conte
|
|
|
14393
14124
|
}
|
|
14394
14125
|
return matchingBlocks.size > 0;
|
|
14395
14126
|
};
|
|
14127
|
+
const doMatchingNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context) => {
|
|
14128
|
+
const functionCfg = context.cfg.cfgFor(owner);
|
|
14129
|
+
if (!functionCfg) return false;
|
|
14130
|
+
const matchingBlocks = new Set(matchingNodes.flatMap((matchingNode) => {
|
|
14131
|
+
if (context.cfg.enclosingFunction(matchingNode) !== owner) return [];
|
|
14132
|
+
const matchingBlock = functionCfg.blockOf(matchingNode);
|
|
14133
|
+
return matchingBlock ? [matchingBlock] : [];
|
|
14134
|
+
}));
|
|
14135
|
+
if (matchingBlocks.size === 0) return false;
|
|
14136
|
+
const visitedBlocks = new Set([functionCfg.entry]);
|
|
14137
|
+
const pendingBlocks = [functionCfg.entry];
|
|
14138
|
+
while (pendingBlocks.length > 0) {
|
|
14139
|
+
const currentBlock = pendingBlocks.pop();
|
|
14140
|
+
if (!currentBlock) break;
|
|
14141
|
+
if (matchingBlocks.has(currentBlock)) continue;
|
|
14142
|
+
for (const edge of currentBlock.successors) {
|
|
14143
|
+
if (edge.to === functionCfg.exit) return false;
|
|
14144
|
+
if (visitedBlocks.has(edge.to)) continue;
|
|
14145
|
+
visitedBlocks.add(edge.to);
|
|
14146
|
+
pendingBlocks.push(edge.to);
|
|
14147
|
+
}
|
|
14148
|
+
}
|
|
14149
|
+
return true;
|
|
14150
|
+
};
|
|
14396
14151
|
const removeSynchronouslyReleasedUsages = (callback, usages, context) => {
|
|
14397
14152
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return usages;
|
|
14398
14153
|
if (!isNodeOfType(callback.body, "BlockStatement")) return usages;
|
|
@@ -14635,6 +14390,18 @@ const findPushedResourceCollectionKey = (usage, context) => {
|
|
|
14635
14390
|
return memberNode.property.name === "forEach" || memberNode.property.name === "push";
|
|
14636
14391
|
}) ? resolveExpressionKey(pushCallee.object, context) : null;
|
|
14637
14392
|
};
|
|
14393
|
+
const isWithinAssignmentTarget = (identifier) => {
|
|
14394
|
+
let currentNode = identifier;
|
|
14395
|
+
let parentNode = currentNode.parent;
|
|
14396
|
+
while (parentNode) {
|
|
14397
|
+
if (isNodeOfType(parentNode, "AssignmentExpression")) return parentNode.left === currentNode;
|
|
14398
|
+
if (isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete") return parentNode.argument === currentNode;
|
|
14399
|
+
if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.left === currentNode;
|
|
14400
|
+
currentNode = parentNode;
|
|
14401
|
+
parentNode = currentNode.parent;
|
|
14402
|
+
}
|
|
14403
|
+
return false;
|
|
14404
|
+
};
|
|
14638
14405
|
const resolveStableValue = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14639
14406
|
if (!expression) return null;
|
|
14640
14407
|
const unwrappedExpression = stripParenExpression(expression);
|
|
@@ -14682,8 +14449,8 @@ const isSynchronousIteratorCallback = (functionNode) => {
|
|
|
14682
14449
|
return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments?.[0] === functionNode;
|
|
14683
14450
|
};
|
|
14684
14451
|
const findEnclosingForEachCall = (node) => {
|
|
14685
|
-
const callbackNode =
|
|
14686
|
-
if (!callbackNode
|
|
14452
|
+
const callbackNode = findEnclosingFunction$1(node);
|
|
14453
|
+
if (!callbackNode) return null;
|
|
14687
14454
|
const callNode = callbackNode.parent;
|
|
14688
14455
|
if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments?.[0] !== callbackNode) return null;
|
|
14689
14456
|
const callee = stripParenExpression(callNode.callee);
|
|
@@ -14758,7 +14525,7 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
|
|
|
14758
14525
|
const helperFunction = isNodeOfType(stableHelperFunction, "Identifier") ? resolveSingleAssignedCleanupFunction(stableHelperFunction, usage, context) : stableHelperFunction;
|
|
14759
14526
|
if (helperFunction && isFunctionLike$1(helperFunction) && !helperFunction.async && !helperFunction.generator && doesCleanupFunctionReleaseUsage(helperFunction, usage, context, new Set(visitedFunctions))) matchingLoopOrHelperAnchors.push(cleanupCall);
|
|
14760
14527
|
});
|
|
14761
|
-
return didCleanupFunctionMatch ||
|
|
14528
|
+
return didCleanupFunctionMatch || doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, matchingLoopOrHelperAnchors, context);
|
|
14762
14529
|
};
|
|
14763
14530
|
const doesBoundCleanupReleaseUsage = (expression, usage, context) => {
|
|
14764
14531
|
const callExpression = stripParenExpression(expression);
|
|
@@ -14787,7 +14554,7 @@ const callbackReturnsCleanupForUsage = (callback, usage, context) => {
|
|
|
14787
14554
|
walkInsideStatementBlocks(callback.body, (child) => {
|
|
14788
14555
|
if (isNodeOfType(child, "ReturnStatement") && child.argument && doesReturnedValueReleaseUsage(stripParenExpression(child.argument))) matchingCleanupReturns.push(child);
|
|
14789
14556
|
});
|
|
14790
|
-
return
|
|
14557
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
|
|
14791
14558
|
};
|
|
14792
14559
|
const doesTestRequireLiveExpressionKey = (test, expressionKey, context) => {
|
|
14793
14560
|
if (resolveExpressionKey(test, context) === expressionKey) return true;
|
|
@@ -14830,7 +14597,7 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
|
|
|
14830
14597
|
const helperFunction = resolveStableValue(child.callee, context);
|
|
14831
14598
|
if (helperFunction && isFunctionLike$1(helperFunction) && doesCleanupFunctionReleaseUsage(helperFunction, usage, context)) matchingReleaseAnchors.push(handleGuard ?? child);
|
|
14832
14599
|
});
|
|
14833
|
-
return
|
|
14600
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
|
|
14834
14601
|
};
|
|
14835
14602
|
const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
|
|
14836
14603
|
const componentFunction = findEnclosingFunction$1(callback);
|
|
@@ -15081,7 +14848,7 @@ const hasGuardedRefOwnedNestedCleanup = (callback, usage, cleanupReturns, contex
|
|
|
15081
14848
|
const usageFunction = findEnclosingFunction$1(usage.node);
|
|
15082
14849
|
const usageExpression = findTransparentExpressionRoot(usage.node);
|
|
15083
14850
|
const usageAssignment = usageExpression.parent;
|
|
15084
|
-
if (usage.kind !== "subscribe" && usage.kind !== "timer" || usage.handleKey === null || !usageFunction || !isFunctionLike$1(usageFunction) || usageFunction === callback || usageFunction.async || usageFunction.generator || !isNodeOfType(usageAssignment, "AssignmentExpression") || usageAssignment.operator !== "=" || usageAssignment.right !== usageExpression || !resolveReactRefSymbol(stripParenExpression(usageAssignment.left), context.scopes) || !collectSynchronouslyEffectInvokedFunctions(callback).has(usageFunction) || !cleanupReturnsReleaseUsage(cleanupReturns, usage, context) || !
|
|
14851
|
+
if (usage.kind !== "subscribe" && usage.kind !== "timer" || usage.handleKey === null || !usageFunction || !isFunctionLike$1(usageFunction) || usageFunction === callback || usageFunction.async || usageFunction.generator || !isNodeOfType(usageAssignment, "AssignmentExpression") || usageAssignment.operator !== "=" || usageAssignment.right !== usageExpression || !resolveReactRefSymbol(stripParenExpression(usageAssignment.left), context.scopes) || !collectSynchronouslyEffectInvokedFunctions(callback).has(usageFunction) || !cleanupReturnsReleaseUsage(cleanupReturns, usage, context) || !doMatchingNodesCoverEveryPathFromFunctionEntry(callback, cleanupReturns, context)) return false;
|
|
15085
14852
|
const cleanupFunctions = cleanupReturns.flatMap((cleanupReturn) => {
|
|
15086
14853
|
if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return [];
|
|
15087
14854
|
const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
|
|
@@ -15129,7 +14896,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
|
|
|
15129
14896
|
});
|
|
15130
14897
|
}
|
|
15131
14898
|
});
|
|
15132
|
-
if (!
|
|
14899
|
+
if (!doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, globalReleaseProofs.map((releaseProof) => releaseProof.anchor), context)) return false;
|
|
15133
14900
|
globalReleaseProofsByCleanup.set(cleanupFunction, globalReleaseProofs);
|
|
15134
14901
|
}
|
|
15135
14902
|
const handleAssignments = handleSymbol.references.filter((reference) => isWithinAssignmentTarget(reference.identifier));
|
|
@@ -15332,7 +15099,9 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
|
|
|
15332
15099
|
const nodeParameterKey = resolveExpressionKey(usageFunction.params?.[0], context);
|
|
15333
15100
|
const releaseReceiverKey = resolveExpressionKey(releaseCallee.object, context);
|
|
15334
15101
|
if (registrationReceiverKey === null || registrationReceiverKey !== nodeParameterKey || releaseReceiverKey === null || usage.eventKey === null || usage.eventKey !== resolveExpressionKey(releaseCall.arguments?.[0], context) || usage.handlerKey === null || usage.handlerKey !== resolveExpressionKey(releaseCall.arguments?.[1], context)) return false;
|
|
15335
|
-
|
|
15102
|
+
const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15103
|
+
const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15104
|
+
if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
|
|
15336
15105
|
const releaseStart = getRangeStart(releaseCall);
|
|
15337
15106
|
const matchingOwnershipAssignments = [];
|
|
15338
15107
|
const usageFunctionBody = usageFunction.body;
|
|
@@ -15342,127 +15111,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
|
|
|
15342
15111
|
});
|
|
15343
15112
|
const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
|
|
15344
15113
|
const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
|
|
15345
|
-
return
|
|
15346
|
-
};
|
|
15347
|
-
const findDirectExhaustiveForEachCleanupFunction = (releaseNode, requiredCollectionKeys, context) => {
|
|
15348
|
-
let currentNode = findTransparentExpressionRoot(releaseNode);
|
|
15349
|
-
const visitedFunctions = /* @__PURE__ */ new Set();
|
|
15350
|
-
const replayedCollectionKeys = /* @__PURE__ */ new Set();
|
|
15351
|
-
while (true) {
|
|
15352
|
-
const ownerFunction = findEnclosingFunction$1(currentNode);
|
|
15353
|
-
if (!ownerFunction || !isFunctionLike$1(ownerFunction) || visitedFunctions.has(ownerFunction)) return null;
|
|
15354
|
-
visitedFunctions.add(ownerFunction);
|
|
15355
|
-
const isDirectConciseBody = ownerFunction.body === currentNode;
|
|
15356
|
-
const statementNode = currentNode.parent;
|
|
15357
|
-
const isDirectBlockStatement = isNodeOfType(ownerFunction.body, "BlockStatement") && isNodeOfType(statementNode, "ExpressionStatement") && statementNode.parent === ownerFunction.body;
|
|
15358
|
-
if (!isDirectConciseBody && !isDirectBlockStatement || !doNodesCoverEveryPathFromFunctionEntry(ownerFunction, [isDirectBlockStatement ? statementNode : currentNode], context)) return null;
|
|
15359
|
-
const forEachCall = findEnclosingForEachCall(ownerFunction);
|
|
15360
|
-
if (!forEachCall) return replayedCollectionKeys.size === requiredCollectionKeys.size && isReturnedEffectCleanupFunction(ownerFunction, context) ? ownerFunction : null;
|
|
15361
|
-
const forEachCallee = stripParenExpression(forEachCall.callee);
|
|
15362
|
-
if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
|
|
15363
|
-
const collectionKey = resolveExpressionKey(forEachCallee.object, context);
|
|
15364
|
-
if (!collectionKey || !requiredCollectionKeys.has(collectionKey)) return null;
|
|
15365
|
-
replayedCollectionKeys.add(collectionKey);
|
|
15366
|
-
currentNode = findTransparentExpressionRoot(forEachCall);
|
|
15367
|
-
}
|
|
15368
|
-
};
|
|
15369
|
-
const collectReplayOwnerFunctions = (usageNode) => {
|
|
15370
|
-
const ownerFunctions = /* @__PURE__ */ new Set();
|
|
15371
|
-
let currentNode = usageNode;
|
|
15372
|
-
while (true) {
|
|
15373
|
-
const ownerFunction = findEnclosingFunction$1(currentNode);
|
|
15374
|
-
if (!ownerFunction || !isFunctionLike$1(ownerFunction) || ownerFunctions.has(ownerFunction)) break;
|
|
15375
|
-
ownerFunctions.add(ownerFunction);
|
|
15376
|
-
const forEachCall = findEnclosingForEachCall(ownerFunction);
|
|
15377
|
-
if (!forEachCall) break;
|
|
15378
|
-
currentNode = forEachCall;
|
|
15379
|
-
}
|
|
15380
|
-
return ownerFunctions;
|
|
15381
|
-
};
|
|
15382
|
-
const hasCollectionMutationBeforeRelease = (usageNode, releaseNode, collectionKeys, context) => {
|
|
15383
|
-
const usageStart = getRangeStart(usageNode);
|
|
15384
|
-
const releaseStart = getRangeStart(releaseNode);
|
|
15385
|
-
if (usageStart === null || releaseStart === null) return true;
|
|
15386
|
-
const setupOwnerFunctions = collectReplayOwnerFunctions(usageNode);
|
|
15387
|
-
const cleanupOwnerFunctions = collectReplayOwnerFunctions(releaseNode);
|
|
15388
|
-
let programNode = usageNode;
|
|
15389
|
-
while (programNode.parent) programNode = programNode.parent;
|
|
15390
|
-
let didFindMutation = false;
|
|
15391
|
-
walkAst(programNode, (child) => {
|
|
15392
|
-
if (didFindMutation) return false;
|
|
15393
|
-
const childStart = getRangeStart(child);
|
|
15394
|
-
if (childStart === null) return;
|
|
15395
|
-
const ownerFunction = context.cfg.enclosingFunction(child);
|
|
15396
|
-
if (!ownerFunction) return;
|
|
15397
|
-
const isAfterRegistration = setupOwnerFunctions.has(ownerFunction) && childStart > usageStart;
|
|
15398
|
-
const isBeforeRelease = cleanupOwnerFunctions.has(ownerFunction) && childStart < releaseStart;
|
|
15399
|
-
if (!isAfterRegistration && !isBeforeRelease) return;
|
|
15400
|
-
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
15401
|
-
const assignmentKey = resolveExpressionKey(child.left, context);
|
|
15402
|
-
const assignmentTarget = stripParenExpression(child.left);
|
|
15403
|
-
if (assignmentKey && [...collectionKeys].some((collectionKey) => assignmentKey === collectionKey || assignmentKey === `${collectionKey}.length`) || isNodeOfType(assignmentTarget, "MemberExpression") && assignmentTarget.computed && collectionKeys.has(resolveExpressionKey(assignmentTarget.object, context) ?? "")) {
|
|
15404
|
-
didFindMutation = true;
|
|
15405
|
-
return false;
|
|
15406
|
-
}
|
|
15407
|
-
return;
|
|
15408
|
-
}
|
|
15409
|
-
if (isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
|
|
15410
|
-
const deletedMember = stripParenExpression(child.argument);
|
|
15411
|
-
if (!isNodeOfType(deletedMember, "MemberExpression")) return;
|
|
15412
|
-
if (collectionKeys.has(resolveExpressionKey(deletedMember.object, context) ?? "")) {
|
|
15413
|
-
didFindMutation = true;
|
|
15414
|
-
return false;
|
|
15415
|
-
}
|
|
15416
|
-
return;
|
|
15417
|
-
}
|
|
15418
|
-
if (isNodeOfType(child, "UpdateExpression")) {
|
|
15419
|
-
const updatedKey = resolveExpressionKey(child.argument, context);
|
|
15420
|
-
if (updatedKey && [...collectionKeys].some((collectionKey) => updatedKey === `${collectionKey}.length`)) {
|
|
15421
|
-
didFindMutation = true;
|
|
15422
|
-
return false;
|
|
15423
|
-
}
|
|
15424
|
-
return;
|
|
15425
|
-
}
|
|
15426
|
-
if (!isNodeOfType(child, "CallExpression")) return;
|
|
15427
|
-
const callee = stripParenExpression(child.callee);
|
|
15428
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES.has(callee.property.name) && !REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES.has(callee.property.name) || !collectionKeys.has(resolveExpressionKey(callee.object, context) ?? "")) return;
|
|
15429
|
-
didFindMutation = true;
|
|
15430
|
-
return false;
|
|
15431
|
-
});
|
|
15432
|
-
return didFindMutation;
|
|
15433
|
-
};
|
|
15434
|
-
const usesUnaryListenerSignature = (registrationCall, releaseCall) => getCalleeName$1(registrationCall) === "addListener" && registrationCall.arguments?.length === 1 && releaseCall.arguments?.length === 1;
|
|
15435
|
-
const hasSafeForEachProjectionCleanup = (registrationCall, releaseCall, context) => {
|
|
15436
|
-
const registrationCallee = stripParenExpression(registrationCall.callee);
|
|
15437
|
-
const releaseCallee = stripParenExpression(releaseCall.callee);
|
|
15438
|
-
if (!isNodeOfType(registrationCallee, "MemberExpression") || !isNodeOfType(releaseCallee, "MemberExpression")) return true;
|
|
15439
|
-
const registrationVerbName = getCalleeName$1(registrationCall);
|
|
15440
|
-
const releaseVerbName = getCalleeName$1(releaseCall);
|
|
15441
|
-
const releaseHandler = releaseCall.arguments?.[usesUnaryListenerSignature(registrationCall, releaseCall) ? 0 : 1];
|
|
15442
|
-
const releaseFunction = findEnclosingFunction$1(releaseCall);
|
|
15443
|
-
const registrationEventKey = resolveResourceIdentityKey(registrationCall.arguments?.[0], context);
|
|
15444
|
-
const releaseEventKey = resolveResourceIdentityKey(releaseCall.arguments?.[0], context);
|
|
15445
|
-
const doesHandlerlessOffReleaseEveryRegistration = releaseVerbName === "off" && !releaseHandler && (releaseCall.arguments?.length === 0 || registrationEventKey !== null && registrationEventKey === releaseEventKey);
|
|
15446
|
-
if (Boolean(releaseFunction && isFunctionLike$1(releaseFunction) && isReturnedEffectCleanupFunction(releaseFunction, context) && doNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseCall], context)) && (releaseVerbName !== null && UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName) || doesHandlerlessOffReleaseEveryRegistration)) return true;
|
|
15447
|
-
const projections = [
|
|
15448
|
-
registrationCallee.object,
|
|
15449
|
-
registrationCall.arguments?.[0],
|
|
15450
|
-
registrationCall.arguments?.[1],
|
|
15451
|
-
releaseCallee.object,
|
|
15452
|
-
releaseCall.arguments?.[0],
|
|
15453
|
-
releaseCall.arguments?.[1]
|
|
15454
|
-
].flatMap((expression) => {
|
|
15455
|
-
const projection = resolveForEachProjection(expression, context);
|
|
15456
|
-
return projection ? [projection] : [];
|
|
15457
|
-
});
|
|
15458
|
-
if (registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener") for (const optionsNode of [registrationCall.arguments?.[2], releaseCall.arguments?.[2]]) {
|
|
15459
|
-
const captureProjection = resolveEventListenerCaptureProjection(optionsNode, context);
|
|
15460
|
-
if (captureProjection) projections.push(captureProjection);
|
|
15461
|
-
}
|
|
15462
|
-
if (projections.length === 0) return true;
|
|
15463
|
-
const collectionKeys = new Set(projections.map((projection) => projection.collectionKey));
|
|
15464
|
-
if (!findDirectExhaustiveForEachCleanupFunction(releaseCall, collectionKeys, context)) return false;
|
|
15465
|
-
return !hasCollectionMutationBeforeRelease(registrationCall, releaseCall, collectionKeys, context);
|
|
15114
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
|
|
15466
15115
|
};
|
|
15467
15116
|
const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
15468
15117
|
const callNode = isNodeOfType(node, "ChainExpression") ? node.expression : node;
|
|
@@ -15479,8 +15128,8 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15479
15128
|
const releaseVerbName = getReleaseVerbName(callNode);
|
|
15480
15129
|
if (!releaseVerbName) return false;
|
|
15481
15130
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
|
|
15482
|
-
const releaseReceiverKey =
|
|
15483
|
-
const releaseEventKey =
|
|
15131
|
+
const releaseReceiverKey = resolveExpressionKey(callee.object, context);
|
|
15132
|
+
const releaseEventKey = resolveExpressionKey(callNode.arguments?.[0], context);
|
|
15484
15133
|
const pairedReleaseVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
|
|
15485
15134
|
const pushedResourceCollectionKey = findPushedResourceCollectionKey(usage, context);
|
|
15486
15135
|
const releaseReceiverForOfStatement = findForOfStatementForIteratorExpression(callee.object, context);
|
|
@@ -15490,13 +15139,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15490
15139
|
if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
|
|
15491
15140
|
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;
|
|
15492
15141
|
if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
|
|
15493
|
-
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;
|
|
15494
15142
|
if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
|
|
15495
|
-
if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
|
|
15496
|
-
if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
|
|
15497
|
-
if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
|
|
15498
|
-
}
|
|
15499
|
-
if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
|
|
15500
15143
|
if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
|
|
15501
15144
|
if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
|
|
15502
15145
|
const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
|
|
@@ -15506,9 +15149,6 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15506
15149
|
if (isAssignmentFormForOfIteratorReference(usageEventArgument, context) || isAssignmentFormForOfIteratorReference(releaseEventArgument, context)) return false;
|
|
15507
15150
|
if (usage.eventKey !== null && releaseEventKey !== null && usage.eventKey !== releaseEventKey) {
|
|
15508
15151
|
if (!isNodeOfType(usage.node, "CallExpression")) return false;
|
|
15509
|
-
const registrationEventProjectionKey = resolveForEachProjectionKey(usage.node.arguments?.[0], context);
|
|
15510
|
-
const releaseEventProjectionKey = resolveForEachProjectionKey(callNode.arguments?.[0], context);
|
|
15511
|
-
if ((registrationEventProjectionKey !== null || releaseEventProjectionKey !== null) && registrationEventProjectionKey !== releaseEventProjectionKey) return false;
|
|
15512
15152
|
const usageForOfStatement = findForOfStatementForIteratorExpression(usageEventArgument, context);
|
|
15513
15153
|
const releaseForOfStatement = findForOfStatementForIteratorExpression(releaseEventArgument, context);
|
|
15514
15154
|
if (usageForOfStatement === null !== (releaseForOfStatement === null)) return false;
|
|
@@ -15522,7 +15162,9 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15522
15162
|
const registrationCallee = stripParenExpression(usage.node.callee);
|
|
15523
15163
|
if (!isNodeOfType(registrationCallee, "MemberExpression")) return false;
|
|
15524
15164
|
if (!isStableLoopReceiver(registrationCallee.object, context) || !isStableLoopReceiver(callee.object, context) || registrationHandlerSymbolId === null || registrationHandlerSymbolId !== releaseHandlerSymbolId) return false;
|
|
15525
|
-
|
|
15165
|
+
const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15166
|
+
const releaseCapture = resolveEventListenerCapture(callNode.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15167
|
+
if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
|
|
15526
15168
|
if (!isDirectExhaustiveForOfRelease(callNode, releaseForOfStatement)) return false;
|
|
15527
15169
|
}
|
|
15528
15170
|
}
|
|
@@ -15531,28 +15173,27 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15531
15173
|
return isNodeOfType(handlerArgument, "Literal") && handlerArgument.value === null;
|
|
15532
15174
|
}
|
|
15533
15175
|
if (releaseVerbName === "removeEventListener" || releaseVerbName === "removeListener" || releaseVerbName === "off") {
|
|
15534
|
-
const
|
|
15535
|
-
const releaseHandler =
|
|
15176
|
+
const usesUnaryListenerSignature = usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1 && callNode.arguments?.length === 1;
|
|
15177
|
+
const releaseHandler = usesUnaryListenerSignature ? callNode.arguments?.[0] : callNode.arguments?.[1];
|
|
15536
15178
|
if (!releaseHandler) return releaseVerbName === "off";
|
|
15537
|
-
const expectedHandlerKey =
|
|
15538
|
-
const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[
|
|
15539
|
-
return expectedHandlerKey !== null &&
|
|
15179
|
+
const expectedHandlerKey = usesUnaryListenerSignature ? usage.eventKey : usage.handlerKey;
|
|
15180
|
+
const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignature ? 0 : 1] : null;
|
|
15181
|
+
return expectedHandlerKey !== null && resolveExpressionKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
|
|
15540
15182
|
}
|
|
15541
15183
|
if (releaseVerbName === "unobserve" && usage.eventKey !== null) return releaseEventKey === usage.eventKey;
|
|
15542
15184
|
return true;
|
|
15543
15185
|
};
|
|
15544
15186
|
const matchesPairedReleaseVerb = (releaseVerbName, pairedVerbNames) => pairedVerbNames.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName);
|
|
15545
15187
|
const isReturnedEffectCleanupFunction = (functionNode, context) => {
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
return isReturned;
|
|
15188
|
+
let currentNode = functionNode;
|
|
15189
|
+
let parentNode = currentNode.parent;
|
|
15190
|
+
while (isNodeOfType(parentNode, "ChainExpression") || isNodeOfType(parentNode, "TSAsExpression") || isNodeOfType(parentNode, "TSNonNullExpression")) {
|
|
15191
|
+
currentNode = parentNode;
|
|
15192
|
+
parentNode = currentNode.parent;
|
|
15193
|
+
}
|
|
15194
|
+
const effectCallback = isNodeOfType(parentNode, "ReturnStatement") && parentNode.argument === currentNode ? findEnclosingFunction$1(parentNode) : isNodeOfType(parentNode, "ArrowFunctionExpression") && parentNode.body === currentNode ? parentNode : null;
|
|
15195
|
+
const effectCall = effectCallback?.parent;
|
|
15196
|
+
return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isReactHookCall(effectCall, CLEANUP_EFFECT_HOOK_NAMES, context.scopes));
|
|
15556
15197
|
};
|
|
15557
15198
|
const isPotentiallyReachableFunction = (functionNode, context) => {
|
|
15558
15199
|
if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode, context)) return true;
|
|
@@ -15642,7 +15283,7 @@ const hasEffectCleanupInvocation = (storage, usage, context) => {
|
|
|
15642
15283
|
if (!cleanupFunction || !cleanupFunctionInvokesRef(cleanupFunction)) return;
|
|
15643
15284
|
matchingReturns.push(child);
|
|
15644
15285
|
});
|
|
15645
|
-
return
|
|
15286
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
|
|
15646
15287
|
};
|
|
15647
15288
|
let didFindInvocation = false;
|
|
15648
15289
|
walkAst(componentFunction.body, (child) => {
|
|
@@ -15694,10 +15335,12 @@ const isRetainedDisposerRefRelease = (releaseNode, usage, context) => {
|
|
|
15694
15335
|
return findRetainedDisposerStorages(disposerFunction, usage, context).some((storage) => isRetainedDisposerStorageEstablished(storage, usage, context) && !hasUnsafeRetainedDisposerOverwrite(storage, usage, context) && (hasEffectCleanupInvocation(storage, usage, context) || hasCallbackRefReplacementInvocation(storage, usage, context)));
|
|
15695
15336
|
};
|
|
15696
15337
|
const isSelfReleasingListenerRelease = (releaseNode, releaseFunction, usage, context) => {
|
|
15697
|
-
if (usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.receiverKey === null || usage.eventKey === null || !isNodeOfType(usage.node, "CallExpression") || !isFunctionLike$1(releaseFunction) || releaseFunction.async || releaseFunction.generator || !isNodeOfType(releaseFunction.body, "BlockStatement") || !
|
|
15338
|
+
if (usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.receiverKey === null || usage.eventKey === null || !isNodeOfType(usage.node, "CallExpression") || !isFunctionLike$1(releaseFunction) || releaseFunction.async || releaseFunction.generator || !isNodeOfType(releaseFunction.body, "BlockStatement") || !doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseNode], context)) return false;
|
|
15339
|
+
const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15698
15340
|
const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
|
|
15699
15341
|
if (!isNodeOfType(releaseCall, "CallExpression")) return false;
|
|
15700
|
-
|
|
15342
|
+
const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15343
|
+
if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
|
|
15701
15344
|
const ownerFunction = findEnclosingFunction$1(releaseFunction);
|
|
15702
15345
|
if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
|
|
15703
15346
|
const triggerRegistrations = [];
|
|
@@ -15922,7 +15565,7 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
|
|
|
15922
15565
|
walkInsideStatementBlocks(effectCallback.body, (child) => {
|
|
15923
15566
|
if (isNodeOfType(child, "ReturnStatement") && child.argument && matchesReturnedCleanup(stripParenExpression(child.argument))) matchingReturns.push(child);
|
|
15924
15567
|
});
|
|
15925
|
-
return
|
|
15568
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
|
|
15926
15569
|
};
|
|
15927
15570
|
const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
|
|
15928
15571
|
const componentFunction = findEnclosingFunction$1(retainedFunction);
|
|
@@ -15965,6 +15608,15 @@ const findUnconditionalReturnStatement = (expression, ownerFunction) => {
|
|
|
15965
15608
|
const returnStatement = expressionRoot.parent;
|
|
15966
15609
|
return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === expressionRoot && findEnclosingFunction$1(returnStatement) === ownerFunction ? returnStatement : null;
|
|
15967
15610
|
};
|
|
15611
|
+
const getFinalSequenceExpressionValue = (expression) => {
|
|
15612
|
+
let finalExpression = stripParenExpression(expression);
|
|
15613
|
+
while (isNodeOfType(finalExpression, "SequenceExpression")) {
|
|
15614
|
+
const sequenceResult = finalExpression.expressions.at(-1);
|
|
15615
|
+
if (!sequenceResult) break;
|
|
15616
|
+
finalExpression = stripParenExpression(sequenceResult);
|
|
15617
|
+
}
|
|
15618
|
+
return finalExpression;
|
|
15619
|
+
};
|
|
15968
15620
|
const doesResourceResultEscape = (resourceNode, allowReturnedResourceEscape, allowConciseReturnEscape, context) => {
|
|
15969
15621
|
if (!allowReturnedResourceEscape) return false;
|
|
15970
15622
|
let currentNode = resourceNode;
|
|
@@ -16213,7 +15865,7 @@ const isReactRefCallbackCleanupOwnedByEffect = (retainedFunction, cleanupFunctio
|
|
|
16213
15865
|
if (child !== returnedCleanupFunction.body && isFunctionLike$1(child)) return false;
|
|
16214
15866
|
if (isNodeOfType(child, "CallExpression") && resolveRefOwnedCleanupFunction(child.callee, context) === cleanupFunction) matchingCalls.push(child);
|
|
16215
15867
|
});
|
|
16216
|
-
return
|
|
15868
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(returnedCleanupFunction, matchingCalls, context);
|
|
16217
15869
|
};
|
|
16218
15870
|
const matchingReturns = [];
|
|
16219
15871
|
walkInsideStatementBlocks(retainedFunction.body, (child) => {
|
|
@@ -22563,49 +22215,25 @@ const jsLengthCheckFirst = defineRule({
|
|
|
22563
22215
|
});
|
|
22564
22216
|
//#endregion
|
|
22565
22217
|
//#region src/plugin/utils/is-proven-global-namespace-reference.ts
|
|
22566
|
-
const GLOBAL_SELF_PROPERTY_NAMES = new Set([
|
|
22567
|
-
"global",
|
|
22568
|
-
"globalThis",
|
|
22569
|
-
"self",
|
|
22570
|
-
"window"
|
|
22571
|
-
]);
|
|
22572
22218
|
const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
22573
|
-
|
|
22574
|
-
|
|
22575
|
-
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
22580
|
-
const declaration = symbol.declarationNode;
|
|
22581
|
-
const initializer = declaration.init;
|
|
22582
|
-
if (!initializer) return false;
|
|
22583
|
-
const isDirectIdentifierAlias = isNodeOfType(declaration.id, "Identifier") && declaration.id === symbol.bindingIdentifier && initializer === symbol.initializer;
|
|
22584
|
-
const bindingProperty = symbol.bindingIdentifier.parent;
|
|
22585
|
-
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
22586
|
-
const isDirectGlobalSelfAlias = isNodeOfType(declaration.id, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.value === symbol.bindingIdentifier && bindingProperty.parent === declaration.id && destructuredPropertyName !== null && GLOBAL_SELF_PROPERTY_NAMES.has(destructuredPropertyName);
|
|
22587
|
-
if (!isDirectIdentifierAlias && !isDirectGlobalSelfAlias) return false;
|
|
22588
|
-
visitedSymbolIds.add(symbol.id);
|
|
22589
|
-
currentExpression = initializer;
|
|
22590
|
-
}
|
|
22219
|
+
const strippedExpression = stripParenExpression(expression);
|
|
22220
|
+
if (!isNodeOfType(strippedExpression, "Identifier")) return false;
|
|
22221
|
+
if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
|
|
22222
|
+
const symbol = scopes.symbolFor(strippedExpression);
|
|
22223
|
+
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
22224
|
+
visitedSymbolIds.add(symbol.id);
|
|
22225
|
+
return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
|
|
22591
22226
|
};
|
|
22592
22227
|
const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
const strippedExpression = stripParenExpression(currentExpression);
|
|
22596
|
-
if (!isNodeOfType(strippedExpression, "Identifier")) return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
|
|
22228
|
+
const strippedExpression = stripParenExpression(expression);
|
|
22229
|
+
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
22597
22230
|
if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
|
|
22598
22231
|
const symbol = scopes.symbolFor(strippedExpression);
|
|
22599
|
-
if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
22232
|
+
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
22600
22233
|
visitedSymbolIds.add(symbol.id);
|
|
22601
|
-
|
|
22602
|
-
const bindingIdentifier = symbol.bindingIdentifier;
|
|
22603
|
-
const bindingProperty = bindingIdentifier.parent;
|
|
22604
|
-
if (getDestructuredBindingPropertyName(bindingIdentifier) === namespaceName && isNodeOfType(declaration, "VariableDeclarator") && isNodeOfType(declaration.id, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.value === bindingIdentifier && bindingProperty.parent === declaration.id && declaration.init) return isProvenGlobalObjectReference(declaration.init, scopes);
|
|
22605
|
-
const directInitializer = getDirectConstInitializer(symbol);
|
|
22606
|
-
if (!directInitializer) return false;
|
|
22607
|
-
currentExpression = directInitializer;
|
|
22234
|
+
return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
|
|
22608
22235
|
}
|
|
22236
|
+
return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
|
|
22609
22237
|
};
|
|
22610
22238
|
//#endregion
|
|
22611
22239
|
//#region src/plugin/rules/js-performance/js-min-max-loop.ts
|
|
@@ -34867,19 +34495,6 @@ const noCascadingSetState = defineRetiredRule({
|
|
|
34867
34495
|
recommendation: "Retired: React batches synchronous state updates from one effect into the same follow-up commit, so setter count does not prove repeated redraws."
|
|
34868
34496
|
});
|
|
34869
34497
|
//#endregion
|
|
34870
|
-
//#region src/plugin/utils/get-react-use-callback-call.ts
|
|
34871
|
-
const getReactUseCallbackCall = (expression, scopes) => {
|
|
34872
|
-
const identifier = stripParenExpression(expression);
|
|
34873
|
-
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
34874
|
-
const symbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
34875
|
-
if (symbol?.kind !== "const" || !symbol.initializer) return null;
|
|
34876
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
34877
|
-
return isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", scopes, {
|
|
34878
|
-
allowGlobalReactNamespace: true,
|
|
34879
|
-
resolveNamedAliases: true
|
|
34880
|
-
}) ? initializer : null;
|
|
34881
|
-
};
|
|
34882
|
-
//#endregion
|
|
34883
34498
|
//#region src/plugin/rules/state-and-effects/utils/create-state-trigger-reachability.ts
|
|
34884
34499
|
const getRefCurrentSymbol = (node, context) => {
|
|
34885
34500
|
const expression = stripParenExpression(node);
|
|
@@ -35408,8 +35023,19 @@ const isBuiltinNamespaceCallee = (callee) => {
|
|
|
35408
35023
|
}
|
|
35409
35024
|
return false;
|
|
35410
35025
|
};
|
|
35026
|
+
const getReactUseCallbackSource = (reference, context) => {
|
|
35027
|
+
const identifier = reference.identifier;
|
|
35028
|
+
const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
|
|
35029
|
+
if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
|
|
35030
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
35031
|
+
if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
|
|
35032
|
+
allowGlobalReactNamespace: true,
|
|
35033
|
+
resolveNamedAliases: true
|
|
35034
|
+
})) return initializer;
|
|
35035
|
+
return null;
|
|
35036
|
+
};
|
|
35411
35037
|
const getDependencyStateRefs = (analysis, context, dependencyReference) => {
|
|
35412
|
-
const useCallbackCall =
|
|
35038
|
+
const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
|
|
35413
35039
|
if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
|
|
35414
35040
|
const dependencyList = useCallbackCall.arguments?.[1];
|
|
35415
35041
|
if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
|
|
@@ -38923,17 +38549,6 @@ const noDynamicImportPath = defineRule({
|
|
|
38923
38549
|
})
|
|
38924
38550
|
});
|
|
38925
38551
|
//#endregion
|
|
38926
|
-
//#region src/plugin/utils/get-require-call-source.ts
|
|
38927
|
-
const getRequireCallSource = (expression) => {
|
|
38928
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
38929
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
|
|
38930
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
|
|
38931
|
-
if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
|
|
38932
|
-
const [firstArgument] = unwrappedExpression.arguments ?? [];
|
|
38933
|
-
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
38934
|
-
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
38935
|
-
};
|
|
38936
|
-
//#endregion
|
|
38937
38552
|
//#region src/plugin/rules/state-and-effects/utils/is-cleanup-return.ts
|
|
38938
38553
|
const ITERATOR_CALLBACK_METHOD_NAMES = new Set([
|
|
38939
38554
|
"each",
|
|
@@ -39078,16 +38693,7 @@ const collectDependencyStateSymbolIds = (effectNode, stateSymbolIds, scopes) =>
|
|
|
39078
38693
|
}
|
|
39079
38694
|
return dependencyStateSymbolIds;
|
|
39080
38695
|
};
|
|
39081
|
-
const
|
|
39082
|
-
const localFunction = resolveExactLocalFunction(expression, scopes);
|
|
39083
|
-
if (localFunction) return localFunction;
|
|
39084
|
-
const useCallbackCall = getReactUseCallbackCall(expression, scopes);
|
|
39085
|
-
if (!useCallbackCall) return null;
|
|
39086
|
-
const callback = useCallbackCall.arguments[0];
|
|
39087
|
-
if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
|
|
39088
|
-
return resolveExactLocalFunction(callback, scopes);
|
|
39089
|
-
};
|
|
39090
|
-
const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeStableCallbacks = true) => {
|
|
38696
|
+
const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
|
|
39091
38697
|
const analysisFunctions = new Set([effectCallback]);
|
|
39092
38698
|
const pendingFunctions = [effectCallback];
|
|
39093
38699
|
while (pendingFunctions.length > 0) {
|
|
@@ -39095,9 +38701,9 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeSta
|
|
|
39095
38701
|
if (!currentFunction || !isFunctionLike$1(currentFunction)) continue;
|
|
39096
38702
|
walkInsideStatementBlocks(currentFunction.body, (child) => {
|
|
39097
38703
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
39098
|
-
const invokedFunction =
|
|
38704
|
+
const invokedFunction = resolveExactLocalFunction(child.callee, scopes);
|
|
39099
38705
|
if (!invokedFunction || analysisFunctions.has(invokedFunction)) return;
|
|
39100
|
-
if (isFunctionLike$1(invokedFunction) &&
|
|
38706
|
+
if (isFunctionLike$1(invokedFunction) && invokedFunction.async) return;
|
|
39101
38707
|
analysisFunctions.add(invokedFunction);
|
|
39102
38708
|
pendingFunctions.push(invokedFunction);
|
|
39103
38709
|
});
|
|
@@ -39110,51 +38716,7 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
|
|
|
39110
38716
|
walkInsideStatementBlocks(analysisFunction.body, visitor);
|
|
39111
38717
|
}
|
|
39112
38718
|
};
|
|
39113
|
-
const
|
|
39114
|
-
if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier")) return null;
|
|
39115
|
-
const setterSymbol = resolveConstIdentifierAlias(node.callee, scopes, true);
|
|
39116
|
-
return setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) ?? null : null;
|
|
39117
|
-
};
|
|
39118
|
-
const collectStateWriteAnalysisFunctions = (effectCallback, scopes, setterSymbolIdToStateName) => {
|
|
39119
|
-
const ordinaryAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes, false);
|
|
39120
|
-
const fullAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes);
|
|
39121
|
-
const stableAnalysisFunctions = [...fullAnalysisFunctions].filter((analysisFunction) => !ordinaryAnalysisFunctions.has(analysisFunction));
|
|
39122
|
-
if (stableAnalysisFunctions.length === 0) return ordinaryAnalysisFunctions;
|
|
39123
|
-
let hasUnprovenObservableWork = stableAnalysisFunctions.some((analysisFunction) => {
|
|
39124
|
-
if (!isFunctionLike$1(analysisFunction)) return false;
|
|
39125
|
-
let hasExecutableDefault = false;
|
|
39126
|
-
for (const parameter of analysisFunction.params) {
|
|
39127
|
-
walkInsideStatementBlocks(parameter, (child) => {
|
|
39128
|
-
if (isNodeOfType(child, "CallExpression") || isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "ImportExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "TaggedTemplateExpression") || isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") hasExecutableDefault = true;
|
|
39129
|
-
});
|
|
39130
|
-
if (hasExecutableDefault) return true;
|
|
39131
|
-
}
|
|
39132
|
-
return false;
|
|
39133
|
-
});
|
|
39134
|
-
const stableWrittenStateNames = /* @__PURE__ */ new Set();
|
|
39135
|
-
const allWrittenStateNames = /* @__PURE__ */ new Set();
|
|
39136
|
-
visitSynchronousFunctionBodies(fullAnalysisFunctions, (child) => {
|
|
39137
|
-
const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
|
|
39138
|
-
if (writtenStateName) allWrittenStateNames.add(writtenStateName);
|
|
39139
|
-
});
|
|
39140
|
-
visitSynchronousFunctionBodies(new Set(stableAnalysisFunctions), (child) => {
|
|
39141
|
-
if (hasUnprovenObservableWork) return;
|
|
39142
|
-
if (isNodeOfType(child, "CallExpression")) {
|
|
39143
|
-
const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
|
|
39144
|
-
if (writtenStateName) {
|
|
39145
|
-
stableWrittenStateNames.add(writtenStateName);
|
|
39146
|
-
return;
|
|
39147
|
-
}
|
|
39148
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
|
|
39149
|
-
if (invokedFunction && fullAnalysisFunctions.has(invokedFunction)) return;
|
|
39150
|
-
hasUnprovenObservableWork = true;
|
|
39151
|
-
return;
|
|
39152
|
-
}
|
|
39153
|
-
if (isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "ImportExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "TaggedTemplateExpression") || isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") hasUnprovenObservableWork = true;
|
|
39154
|
-
});
|
|
39155
|
-
return !hasUnprovenObservableWork && stableWrittenStateNames.size === 1 && allWrittenStateNames.size === 1 ? fullAnalysisFunctions : ordinaryAnalysisFunctions;
|
|
39156
|
-
};
|
|
39157
|
-
const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set(), additionalStateValues = /* @__PURE__ */ new Map()) => {
|
|
38719
|
+
const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
39158
38720
|
const unwrappedExpression = stripParenExpression(expression);
|
|
39159
38721
|
if (isNodeOfType(unwrappedExpression, "Literal")) {
|
|
39160
38722
|
const literalValue = unwrappedExpression.value;
|
|
@@ -39162,49 +38724,46 @@ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, vi
|
|
|
39162
38724
|
return null;
|
|
39163
38725
|
}
|
|
39164
38726
|
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
39165
|
-
|
|
39166
|
-
if (symbol?.id === stateSymbolId) return stateValue;
|
|
39167
|
-
if (symbol && additionalStateValues.has(symbol.id)) return additionalStateValues.get(symbol.id) ?? null;
|
|
38727
|
+
if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
|
|
39168
38728
|
if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
|
|
39169
|
-
if (unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression)) return { value: NaN };
|
|
39170
38729
|
const immutableSymbol = scopes.symbolFor(unwrappedExpression);
|
|
39171
38730
|
if (immutableSymbol?.kind !== "const" || !immutableSymbol.initializer || !isNodeOfType(immutableSymbol.declarationNode, "VariableDeclarator") || immutableSymbol.declarationNode.id !== immutableSymbol.bindingIdentifier || immutableSymbol.declarationNode.init !== immutableSymbol.initializer || immutableSymbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(immutableSymbol.id)) return null;
|
|
39172
|
-
return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id)
|
|
38731
|
+
return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
|
|
39173
38732
|
}
|
|
39174
38733
|
if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
|
|
39175
38734
|
if (unwrappedExpression.operator === "void") return { value: void 0 };
|
|
39176
38735
|
if (unwrappedExpression.operator !== "!") return null;
|
|
39177
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38736
|
+
const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39178
38737
|
return argumentValue ? { value: !argumentValue.value } : null;
|
|
39179
38738
|
}
|
|
39180
38739
|
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
39181
38740
|
if (isNodeOfType(unwrappedExpression.callee, "Identifier") && unwrappedExpression.callee.name === "Boolean" && scopes.isGlobalReference(unwrappedExpression.callee) && unwrappedExpression.arguments.length === 1 && unwrappedExpression.arguments[0] && !isNodeOfType(unwrappedExpression.arguments[0], "SpreadElement")) {
|
|
39182
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38741
|
+
const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39183
38742
|
return argumentValue ? { value: Boolean(argumentValue.value) } : null;
|
|
39184
38743
|
}
|
|
39185
38744
|
return null;
|
|
39186
38745
|
}
|
|
39187
38746
|
if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
|
|
39188
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38747
|
+
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39189
38748
|
if (!leftValue) return null;
|
|
39190
38749
|
if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
|
|
39191
38750
|
if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
|
|
39192
38751
|
if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
|
|
39193
|
-
return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38752
|
+
return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39194
38753
|
}
|
|
39195
38754
|
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
39196
|
-
const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38755
|
+
const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39197
38756
|
if (!testValue) return null;
|
|
39198
|
-
return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38757
|
+
return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39199
38758
|
}
|
|
39200
38759
|
if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
|
|
39201
|
-
const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38760
|
+
const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39202
38761
|
if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
|
|
39203
38762
|
return null;
|
|
39204
38763
|
}
|
|
39205
38764
|
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
39206
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
39207
|
-
const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38765
|
+
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
38766
|
+
const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39208
38767
|
if (!leftValue || !rightValue) return null;
|
|
39209
38768
|
if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
|
|
39210
38769
|
const areEqual = leftValue.value === rightValue.value;
|
|
@@ -39243,7 +38802,8 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
|
|
|
39243
38802
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
39244
38803
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
39245
38804
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
39246
|
-
const
|
|
38805
|
+
const setterSymbol = resolveConstIdentifierAlias(child.callee, scopes, true);
|
|
38806
|
+
const stateName = setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) : void 0;
|
|
39247
38807
|
if (!stateName) return;
|
|
39248
38808
|
const writeInfo = stateWrites.get(stateName) ?? {
|
|
39249
38809
|
values: /* @__PURE__ */ new Set(),
|
|
@@ -39259,27 +38819,27 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
|
|
|
39259
38819
|
const isGlobalBooleanCall = (node, scopes) => {
|
|
39260
38820
|
return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
|
|
39261
38821
|
};
|
|
39262
|
-
const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes
|
|
38822
|
+
const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
|
|
39263
38823
|
let currentNode = workNode;
|
|
39264
38824
|
while (currentNode.parent) {
|
|
39265
38825
|
const parentNode = currentNode.parent;
|
|
39266
38826
|
if (isFunctionLike$1(parentNode)) break;
|
|
39267
38827
|
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
39268
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue
|
|
38828
|
+
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
39269
38829
|
if (testValue) {
|
|
39270
38830
|
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
39271
38831
|
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
39272
38832
|
}
|
|
39273
38833
|
}
|
|
39274
38834
|
if (isNodeOfType(parentNode, "ConditionalExpression")) {
|
|
39275
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue
|
|
38835
|
+
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
39276
38836
|
if (testValue) {
|
|
39277
38837
|
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
39278
38838
|
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
39279
38839
|
}
|
|
39280
38840
|
}
|
|
39281
38841
|
if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
|
|
39282
|
-
const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue
|
|
38842
|
+
const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
|
|
39283
38843
|
if (leftValue) {
|
|
39284
38844
|
if (parentNode.operator === "&&" && !leftValue.value) return false;
|
|
39285
38845
|
if (parentNode.operator === "||" && leftValue.value) return false;
|
|
@@ -39291,7 +38851,7 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
|
|
|
39291
38851
|
if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
|
|
39292
38852
|
const earlierStatement = parentNode.body[index];
|
|
39293
38853
|
if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
|
|
39294
|
-
if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue
|
|
38854
|
+
if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
|
|
39295
38855
|
}
|
|
39296
38856
|
}
|
|
39297
38857
|
currentNode = parentNode;
|
|
@@ -39301,93 +38861,24 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
|
|
|
39301
38861
|
const isReaderWorkNode = (node, analysisFunctions, scopes) => {
|
|
39302
38862
|
if (isNodeOfType(node, "CallExpression")) {
|
|
39303
38863
|
if (isGlobalBooleanCall(node, scopes)) return false;
|
|
39304
|
-
const invokedFunction =
|
|
38864
|
+
const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
|
|
39305
38865
|
return !invokedFunction || !analysisFunctions.has(invokedFunction);
|
|
39306
38866
|
}
|
|
39307
38867
|
return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
|
|
39308
38868
|
};
|
|
39309
|
-
const
|
|
39310
|
-
if (
|
|
39311
|
-
const
|
|
39312
|
-
|
|
39313
|
-
return knownStateValues;
|
|
39314
|
-
};
|
|
39315
|
-
const buildInvokedFunctionParameterStateValues = (invokedFunction, invocation, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map(), callerParameterStateValues = /* @__PURE__ */ new Map()) => {
|
|
39316
|
-
if (!isFunctionLike$1(invokedFunction)) return /* @__PURE__ */ new Map();
|
|
39317
|
-
const parameterStateValues = /* @__PURE__ */ new Map();
|
|
39318
|
-
const callerKnownStateValues = mergeKnownStateValues(additionalStateValues, callerParameterStateValues);
|
|
39319
|
-
for (let parameterIndex = 0; parameterIndex < invokedFunction.params.length; parameterIndex += 1) {
|
|
39320
|
-
const rawParameter = invokedFunction.params[parameterIndex];
|
|
39321
|
-
const rawArgument = invocation.arguments[parameterIndex];
|
|
39322
|
-
if (rawArgument && isNodeOfType(rawArgument, "SpreadElement")) break;
|
|
39323
|
-
const parameter = isNodeOfType(rawParameter, "AssignmentPattern") ? rawParameter.left : rawParameter;
|
|
39324
|
-
if (!isNodeOfType(parameter, "Identifier")) continue;
|
|
39325
|
-
const parameterSymbol = scopes.symbolFor(parameter);
|
|
39326
|
-
if (!parameterSymbol) continue;
|
|
39327
|
-
let argumentValue = null;
|
|
39328
|
-
if (rawArgument) {
|
|
39329
|
-
argumentValue = readStaticEffectValue(rawArgument, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), callerKnownStateValues);
|
|
39330
|
-
if (argumentValue !== null && argumentValue.value === void 0 && isNodeOfType(rawParameter, "AssignmentPattern")) {
|
|
39331
|
-
const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
|
|
39332
|
-
argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
|
|
39333
|
-
}
|
|
39334
|
-
} else if (isNodeOfType(rawParameter, "AssignmentPattern")) {
|
|
39335
|
-
const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
|
|
39336
|
-
argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
|
|
39337
|
-
} else argumentValue = { value: void 0 };
|
|
39338
|
-
if (argumentValue) parameterStateValues.set(parameterSymbol.id, argumentValue);
|
|
39339
|
-
}
|
|
39340
|
-
return parameterStateValues;
|
|
39341
|
-
};
|
|
39342
|
-
const haveEqualParameterStateValues = (leftValues, rightValues) => {
|
|
39343
|
-
if (leftValues.size !== rightValues.size) return false;
|
|
39344
|
-
for (const [symbolId, leftValue] of leftValues) {
|
|
39345
|
-
const rightValue = rightValues.get(symbolId);
|
|
39346
|
-
if (!rightValue || !Object.is(leftValue.value, rightValue.value)) return false;
|
|
39347
|
-
}
|
|
39348
|
-
return true;
|
|
39349
|
-
};
|
|
39350
|
-
const canReaderWorkRunForStateValues = (readerEffect, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
|
|
39351
|
-
const pendingFrames = [{
|
|
39352
|
-
functionNode: readerEffect.callback,
|
|
39353
|
-
parameterStateValues: /* @__PURE__ */ new Map()
|
|
39354
|
-
}];
|
|
39355
|
-
const visitedParameterStateValues = /* @__PURE__ */ new Map();
|
|
39356
|
-
while (pendingFrames.length > 0) {
|
|
39357
|
-
const frame = pendingFrames.pop();
|
|
39358
|
-
if (!frame || !isFunctionLike$1(frame.functionNode)) continue;
|
|
39359
|
-
const functionParameterStateValues = visitedParameterStateValues.get(frame.functionNode) ?? [];
|
|
39360
|
-
if (functionParameterStateValues.some((previousValues) => haveEqualParameterStateValues(previousValues, frame.parameterStateValues))) continue;
|
|
39361
|
-
functionParameterStateValues.push(frame.parameterStateValues);
|
|
39362
|
-
visitedParameterStateValues.set(frame.functionNode, functionParameterStateValues);
|
|
39363
|
-
const knownStateValues = mergeKnownStateValues(additionalStateValues, frame.parameterStateValues);
|
|
38869
|
+
const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
|
|
38870
|
+
if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
|
|
38871
|
+
for (const writtenValue of writeInfo.values) {
|
|
38872
|
+
const stateValue = { value: writtenValue };
|
|
39364
38873
|
let didFindReachableWork = false;
|
|
39365
|
-
|
|
39366
|
-
if (didFindReachableWork) return;
|
|
39367
|
-
if (
|
|
39368
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
|
|
39369
|
-
if (invokedFunction && readerEffect.analysisFunctions.has(invokedFunction)) {
|
|
39370
|
-
if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) pendingFrames.push({
|
|
39371
|
-
functionNode: invokedFunction,
|
|
39372
|
-
parameterStateValues: buildInvokedFunctionParameterStateValues(invokedFunction, child, stateSymbolId, stateValue, scopes, additionalStateValues, frame.parameterStateValues)
|
|
39373
|
-
});
|
|
39374
|
-
return;
|
|
39375
|
-
}
|
|
39376
|
-
}
|
|
39377
|
-
if (isReaderWorkNode(child, readerEffect.analysisFunctions, scopes) && isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) didFindReachableWork = true;
|
|
38874
|
+
visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
|
|
38875
|
+
if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
|
|
38876
|
+
if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
|
|
39378
38877
|
});
|
|
39379
38878
|
if (didFindReachableWork) return true;
|
|
39380
38879
|
}
|
|
39381
38880
|
return false;
|
|
39382
38881
|
};
|
|
39383
|
-
const canStateWriteReachReaderWork = (writtenStateName, writerEffect, readerEffect, stateSymbolIds, scopes) => {
|
|
39384
|
-
const writeInfo = writerEffect.stateWrites.get(writtenStateName);
|
|
39385
|
-
const stateSymbolId = stateSymbolIds.get(writtenStateName);
|
|
39386
|
-
if (!writeInfo || stateSymbolId === void 0) return true;
|
|
39387
|
-
if (writeInfo.hasUnknownValue) return true;
|
|
39388
|
-
for (const writtenValue of writeInfo.values) if (canReaderWorkRunForStateValues(readerEffect, stateSymbolId, { value: writtenValue }, scopes)) return true;
|
|
39389
|
-
return false;
|
|
39390
|
-
};
|
|
39391
38882
|
const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
|
|
39392
38883
|
const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
|
|
39393
38884
|
"clear",
|
|
@@ -39398,499 +38889,26 @@ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
|
|
|
39398
38889
|
"keys",
|
|
39399
38890
|
"values"
|
|
39400
38891
|
]);
|
|
39401
|
-
const
|
|
39402
|
-
|
|
39403
|
-
|
|
39404
|
-
"
|
|
39405
|
-
|
|
39406
|
-
|
|
39407
|
-
|
|
39408
|
-
["got", new Set(["got"])],
|
|
39409
|
-
["wretch", new Set(["wretch"])],
|
|
39410
|
-
["ofetch", new Set(["ofetch"])],
|
|
39411
|
-
["setTimeout", new Set([
|
|
39412
|
-
"node:timers",
|
|
39413
|
-
"node:timers/promises",
|
|
39414
|
-
"timers",
|
|
39415
|
-
"timers/promises"
|
|
39416
|
-
])],
|
|
39417
|
-
["setInterval", new Set([
|
|
39418
|
-
"node:timers",
|
|
39419
|
-
"node:timers/promises",
|
|
39420
|
-
"timers",
|
|
39421
|
-
"timers/promises"
|
|
39422
|
-
])]
|
|
39423
|
-
]);
|
|
39424
|
-
const EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES = new Map([
|
|
39425
|
-
["axios", "axios"],
|
|
39426
|
-
["cross-fetch", "fetch"],
|
|
39427
|
-
["got", "got"],
|
|
39428
|
-
["ky", "ky"],
|
|
39429
|
-
["node-fetch", "fetch"],
|
|
39430
|
-
["wretch", "wretch"]
|
|
39431
|
-
]);
|
|
39432
|
-
const EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES = new Set([
|
|
39433
|
-
"axios",
|
|
39434
|
-
"cross-fetch",
|
|
39435
|
-
"got",
|
|
39436
|
-
"ky",
|
|
39437
|
-
"node-fetch",
|
|
39438
|
-
"ofetch",
|
|
39439
|
-
"undici",
|
|
39440
|
-
"wretch"
|
|
39441
|
-
]);
|
|
39442
|
-
const EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES = new Set([
|
|
39443
|
-
"axios",
|
|
39444
|
-
"got",
|
|
39445
|
-
"ky",
|
|
39446
|
-
"ofetch",
|
|
39447
|
-
"wretch"
|
|
39448
|
-
]);
|
|
39449
|
-
const EXTERNAL_SYNC_HTTP_METHOD_NAMES = new Set([
|
|
39450
|
-
...EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES,
|
|
39451
|
-
"fetch",
|
|
39452
|
-
"patch",
|
|
39453
|
-
"post",
|
|
39454
|
-
"put",
|
|
39455
|
-
"request"
|
|
39456
|
-
]);
|
|
39457
|
-
const TANSTACK_QUERY_CLIENT_MODULE_SOURCES = new Set(["@tanstack/query-core", "@tanstack/react-query"]);
|
|
39458
|
-
const TANSTACK_QUERY_EXTERNAL_SYNC_METHOD_NAMES = new Set(["fetchQuery", "prefetchQuery"]);
|
|
39459
|
-
const EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES = new Set([
|
|
39460
|
-
...EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS,
|
|
39461
|
-
...SOCKET_CONSTRUCTOR_NAMES_REQUIRING_CLEANUP,
|
|
39462
|
-
"EventTarget",
|
|
39463
|
-
"XMLHttpRequest"
|
|
39464
|
-
]);
|
|
39465
|
-
const getDestructuredBindingDepth = (bindingIdentifier) => {
|
|
39466
|
-
let bindingNode = bindingIdentifier;
|
|
39467
|
-
let depth = 0;
|
|
39468
|
-
while (true) {
|
|
39469
|
-
if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
|
|
39470
|
-
const property = bindingNode.parent;
|
|
39471
|
-
if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return depth;
|
|
39472
|
-
depth += 1;
|
|
39473
|
-
bindingNode = property.parent;
|
|
39474
|
-
}
|
|
39475
|
-
};
|
|
39476
|
-
const isMutatedMemberExpression = (rawMemberExpression) => {
|
|
39477
|
-
let expression = findTransparentExpressionRoot(rawMemberExpression);
|
|
39478
|
-
while (expression.parent) {
|
|
39479
|
-
const parent = expression.parent;
|
|
39480
|
-
if (isNodeOfType(parent, "MemberExpression") && parent.object === expression) {
|
|
39481
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39482
|
-
continue;
|
|
39483
|
-
}
|
|
39484
|
-
if (isNodeOfType(parent, "Property") && parent.value === expression && isNodeOfType(parent.parent, "ObjectPattern")) {
|
|
39485
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39486
|
-
continue;
|
|
39487
|
-
}
|
|
39488
|
-
if (isNodeOfType(parent, "ObjectPattern") && parent.properties.some((property) => property === expression)) {
|
|
39489
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39490
|
-
continue;
|
|
39491
|
-
}
|
|
39492
|
-
if (isNodeOfType(parent, "ArrayPattern") && parent.elements.some((element) => element === expression)) {
|
|
39493
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39494
|
-
continue;
|
|
39495
|
-
}
|
|
39496
|
-
if (isNodeOfType(parent, "RestElement") && parent.argument === expression || isNodeOfType(parent, "AssignmentPattern") && parent.left === expression) {
|
|
39497
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39498
|
-
continue;
|
|
39499
|
-
}
|
|
39500
|
-
break;
|
|
39501
|
-
}
|
|
39502
|
-
const parent = expression.parent;
|
|
39503
|
-
return Boolean(isNodeOfType(parent, "AssignmentExpression") && parent.left === expression || isNodeOfType(parent, "UpdateExpression") && parent.argument === expression || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === expression || (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) && parent.left === expression);
|
|
39504
|
-
};
|
|
39505
|
-
const isReactHookDependencyArgument = (expression, scopes) => {
|
|
39506
|
-
const call = expression.parent;
|
|
39507
|
-
return Boolean(isNodeOfType(call, "CallExpression") && call.arguments[1] === expression && isReactApiCall(call, HOOKS_WITH_DEPS, scopes, {
|
|
39508
|
-
allowGlobalReactNamespace: true,
|
|
39509
|
-
allowUnboundBareCalls: true,
|
|
39510
|
-
resolveNamedAliases: true
|
|
39511
|
-
}));
|
|
39512
|
-
};
|
|
39513
|
-
const isPlainDependencyObjectExpression = (expression) => isNodeOfType(expression, "ObjectExpression") && expression.properties.every((property) => isNodeOfType(property, "Property") && property.kind === "init" && !property.computed && !property.method);
|
|
39514
|
-
const getContainingReactDependencyValue = (rawExpression, containerKind) => {
|
|
39515
|
-
const expression = findTransparentExpressionRoot(rawExpression);
|
|
39516
|
-
const parent = expression.parent;
|
|
39517
|
-
if (isNodeOfType(parent, "ArrayExpression")) return {
|
|
39518
|
-
expression: parent,
|
|
39519
|
-
containerKind: "array"
|
|
39520
|
-
};
|
|
39521
|
-
if (containerKind === "array" && isNodeOfType(parent, "SpreadElement") && parent.argument === expression && isNodeOfType(parent.parent, "ArrayExpression")) return {
|
|
39522
|
-
expression: parent.parent,
|
|
39523
|
-
containerKind: "array"
|
|
39524
|
-
};
|
|
39525
|
-
if (isNodeOfType(parent, "Property") && (parent.value === expression || parent.shorthand && parent.key === expression) && parent.parent && isPlainDependencyObjectExpression(parent.parent)) return {
|
|
39526
|
-
expression: parent.parent,
|
|
39527
|
-
containerKind: "object"
|
|
39528
|
-
};
|
|
39529
|
-
return null;
|
|
39530
|
-
};
|
|
39531
|
-
const getFullArrayRestCopySymbol = (initializer, scopes) => {
|
|
39532
|
-
const declaration = initializer.parent;
|
|
39533
|
-
if (!isNodeOfType(declaration, "VariableDeclarator") || declaration.init !== initializer || !isNodeOfType(declaration.id, "ArrayPattern") || declaration.id.elements.length !== 1) return null;
|
|
39534
|
-
const restElement = declaration.id.elements[0];
|
|
39535
|
-
if (!isNodeOfType(restElement, "RestElement") || !isNodeOfType(restElement.argument, "Identifier")) return null;
|
|
39536
|
-
const symbol = scopes.symbolFor(restElement.argument);
|
|
39537
|
-
return symbol?.kind === "const" && symbol.declarationNode === declaration && symbol.references.every((reference) => reference.flag === "read") ? symbol : null;
|
|
39538
|
-
};
|
|
39539
|
-
const isReactDependencyArrayReference = (initialExpression, scopes) => {
|
|
39540
|
-
const pendingPaths = [{
|
|
39541
|
-
expression: initialExpression,
|
|
39542
|
-
containerKind: null
|
|
39543
|
-
}];
|
|
39544
|
-
const visitedExpressions = /* @__PURE__ */ new Set();
|
|
39545
|
-
let didFindReactDependencyArgument = false;
|
|
39546
|
-
while (pendingPaths.length > 0) {
|
|
39547
|
-
const pendingPath = pendingPaths.pop();
|
|
39548
|
-
if (!pendingPath) continue;
|
|
39549
|
-
const expression = findTransparentExpressionRoot(pendingPath.expression);
|
|
39550
|
-
if (visitedExpressions.has(expression)) continue;
|
|
39551
|
-
visitedExpressions.add(expression);
|
|
39552
|
-
if (pendingPath.containerKind === "array" && isReactHookDependencyArgument(expression, scopes)) {
|
|
39553
|
-
didFindReactDependencyArgument = true;
|
|
39554
|
-
continue;
|
|
39555
|
-
}
|
|
39556
|
-
const containingValue = getContainingReactDependencyValue(expression, pendingPath.containerKind);
|
|
39557
|
-
if (containingValue) {
|
|
39558
|
-
pendingPaths.push(containingValue);
|
|
39559
|
-
continue;
|
|
39560
|
-
}
|
|
39561
|
-
const declaration = expression.parent;
|
|
39562
|
-
if (isNodeOfType(declaration, "VariableDeclarator") && declaration.init === expression && isNodeOfType(declaration.id, "Identifier")) {
|
|
39563
|
-
const symbol = scopes.symbolFor(declaration.id);
|
|
39564
|
-
if (!symbol || getDirectUnreassignedInitializer(symbol) !== expression) return false;
|
|
39565
|
-
if (symbol.references.length === 0) return false;
|
|
39566
|
-
for (const reference of symbol.references) pendingPaths.push({
|
|
39567
|
-
expression: reference.identifier,
|
|
39568
|
-
containerKind: pendingPath.containerKind
|
|
39569
|
-
});
|
|
39570
|
-
continue;
|
|
39571
|
-
}
|
|
39572
|
-
if (pendingPath.containerKind === "array") {
|
|
39573
|
-
const restCopySymbol = getFullArrayRestCopySymbol(expression, scopes);
|
|
39574
|
-
if (restCopySymbol) {
|
|
39575
|
-
if (restCopySymbol.references.length === 0) return false;
|
|
39576
|
-
for (const reference of restCopySymbol.references) pendingPaths.push({
|
|
39577
|
-
expression: reference.identifier,
|
|
39578
|
-
containerKind: "array"
|
|
39579
|
-
});
|
|
39580
|
-
continue;
|
|
39581
|
-
}
|
|
39582
|
-
}
|
|
39583
|
-
return false;
|
|
39584
|
-
}
|
|
39585
|
-
return didFindReactDependencyArgument;
|
|
39586
|
-
};
|
|
39587
|
-
const hasSafeReceiverAliases = (rootSymbols, scopes) => {
|
|
39588
|
-
const pendingSymbols = [...rootSymbols];
|
|
39589
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
39590
|
-
while (pendingSymbols.length > 0) {
|
|
39591
|
-
const symbol = pendingSymbols.pop();
|
|
39592
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) continue;
|
|
39593
|
-
visitedSymbolIds.add(symbol.id);
|
|
39594
|
-
for (const reference of symbol.references) {
|
|
39595
|
-
const expression = findTransparentExpressionRoot(reference.identifier);
|
|
39596
|
-
const container = expression.parent;
|
|
39597
|
-
if (isReactDependencyArrayReference(expression, scopes)) continue;
|
|
39598
|
-
if (isNodeOfType(container, "MemberExpression") && container.object === expression) {
|
|
39599
|
-
if (isMutatedMemberExpression(container)) return false;
|
|
39600
|
-
continue;
|
|
39601
|
-
}
|
|
39602
|
-
if (isNodeOfType(container, "CallExpression") && container.callee === expression) continue;
|
|
39603
|
-
if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "ObjectPattern")) continue;
|
|
39604
|
-
if (isNodeOfType(container, "AssignmentExpression") && container.right === expression && isNodeOfType(container.left, "ObjectPattern")) continue;
|
|
39605
|
-
if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "Identifier")) {
|
|
39606
|
-
const aliasSymbol = scopes.symbolFor(container.id);
|
|
39607
|
-
const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
|
|
39608
|
-
if (!aliasSymbol || aliasInitializer !== expression) return false;
|
|
39609
|
-
pendingSymbols.push(aliasSymbol);
|
|
39610
|
-
continue;
|
|
39611
|
-
}
|
|
39612
|
-
return false;
|
|
38892
|
+
const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
|
|
38893
|
+
if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
|
|
38894
|
+
if (isNodeOfType(returnedValue, "CallExpression")) {
|
|
38895
|
+
if (isNodeOfType(returnedValue.callee, "Identifier")) {
|
|
38896
|
+
const setterSymbol = resolveConstIdentifierAlias(returnedValue.callee, scopes, true);
|
|
38897
|
+
if (setterToStateName.has(returnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
|
|
38898
|
+
if (isSetterIdentifier(returnedValue.callee.name)) return true;
|
|
39613
38899
|
}
|
|
38900
|
+
return isCleanupReturn(returnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
|
|
39614
38901
|
}
|
|
39615
|
-
return true;
|
|
39616
|
-
};
|
|
39617
|
-
const hasProvenGlobalNamespaceReference = (expression, namespaceNames, scopes) => {
|
|
39618
|
-
for (const namespaceName of namespaceNames) if (isProvenGlobalNamespaceReference(expression, namespaceName, scopes)) return true;
|
|
38902
|
+
if (isNodeOfType(returnedValue, "Identifier")) return true;
|
|
39619
38903
|
return false;
|
|
39620
38904
|
};
|
|
39621
|
-
const
|
|
39622
|
-
|
|
39623
|
-
|
|
39624
|
-
if (
|
|
39625
|
-
return
|
|
39626
|
-
};
|
|
39627
|
-
const getUnshadowedRequireSource = (rawExpression, scopes) => {
|
|
39628
|
-
let expression = stripParenExpression(rawExpression);
|
|
39629
|
-
while (isNodeOfType(expression, "MemberExpression")) expression = stripParenExpression(expression.object);
|
|
39630
|
-
if (!isNodeOfType(expression, "CallExpression") || !isNodeOfType(expression.callee, "Identifier") || expression.callee.name !== "require" || !scopes.isGlobalReference(expression.callee)) return null;
|
|
39631
|
-
return getRequireCallSource(rawExpression);
|
|
39632
|
-
};
|
|
39633
|
-
const resolveExternalModuleBinding = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set(), ignoreCommonJsMutation = false) => {
|
|
39634
|
-
let expression = stripParenExpression(rawExpression);
|
|
39635
|
-
let outermostExportedName = null;
|
|
39636
|
-
let exportedPropertyDepth = 0;
|
|
39637
|
-
let resolvedBinding = null;
|
|
39638
|
-
let isCommonJsModuleObject = false;
|
|
39639
|
-
const traversedSymbolIds = new Set(visitedSymbolIds);
|
|
39640
|
-
const commonJsModuleObjectSymbols = /* @__PURE__ */ new Set();
|
|
39641
|
-
while (true) {
|
|
39642
|
-
if (isNodeOfType(expression, "MemberExpression")) {
|
|
39643
|
-
const propertyName = getStaticPropertyName(expression);
|
|
39644
|
-
if (propertyName === null) return null;
|
|
39645
|
-
outermostExportedName ??= propertyName;
|
|
39646
|
-
exportedPropertyDepth += 1;
|
|
39647
|
-
expression = stripParenExpression(expression.object);
|
|
39648
|
-
continue;
|
|
39649
|
-
}
|
|
39650
|
-
const requireSource = getUnshadowedRequireSource(expression, scopes);
|
|
39651
|
-
if (requireSource !== null) {
|
|
39652
|
-
resolvedBinding = {
|
|
39653
|
-
source: requireSource,
|
|
39654
|
-
exportedName: null,
|
|
39655
|
-
isModuleObject: true
|
|
39656
|
-
};
|
|
39657
|
-
isCommonJsModuleObject = true;
|
|
39658
|
-
break;
|
|
39659
|
-
}
|
|
39660
|
-
if (!isNodeOfType(expression, "Identifier")) return null;
|
|
39661
|
-
const symbol = scopes.symbolFor(expression);
|
|
39662
|
-
if (!symbol || traversedSymbolIds.has(symbol.id)) return null;
|
|
39663
|
-
if (symbol.kind === "import") {
|
|
39664
|
-
const importDeclaration = symbol.declarationNode.parent;
|
|
39665
|
-
if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string") return null;
|
|
39666
|
-
if (isNodeOfType(symbol.declarationNode, "ImportSpecifier")) resolvedBinding = {
|
|
39667
|
-
source: importDeclaration.source.value,
|
|
39668
|
-
exportedName: getImportedName(symbol.declarationNode) ?? null,
|
|
39669
|
-
isModuleObject: false
|
|
39670
|
-
};
|
|
39671
|
-
else if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) resolvedBinding = {
|
|
39672
|
-
source: importDeclaration.source.value,
|
|
39673
|
-
exportedName: "default",
|
|
39674
|
-
isModuleObject: false
|
|
39675
|
-
};
|
|
39676
|
-
else if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) resolvedBinding = {
|
|
39677
|
-
source: importDeclaration.source.value,
|
|
39678
|
-
exportedName: null,
|
|
39679
|
-
isModuleObject: true
|
|
39680
|
-
};
|
|
39681
|
-
break;
|
|
39682
|
-
}
|
|
39683
|
-
const importEqualsSource = getTypeScriptImportEqualsSource(symbol);
|
|
39684
|
-
if (importEqualsSource !== null) {
|
|
39685
|
-
resolvedBinding = {
|
|
39686
|
-
source: importEqualsSource,
|
|
39687
|
-
exportedName: null,
|
|
39688
|
-
isModuleObject: true
|
|
39689
|
-
};
|
|
39690
|
-
isCommonJsModuleObject = true;
|
|
39691
|
-
commonJsModuleObjectSymbols.add(symbol);
|
|
39692
|
-
break;
|
|
39693
|
-
}
|
|
39694
|
-
const directInitializer = getDirectUnreassignedInitializer(symbol);
|
|
39695
|
-
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
39696
|
-
const initializer = directInitializer ?? (destructuredPropertyName !== null && symbol.kind === "const" && isNodeOfType(symbol.declarationNode, "VariableDeclarator") ? symbol.declarationNode.init : null);
|
|
39697
|
-
if (!initializer) return null;
|
|
39698
|
-
traversedSymbolIds.add(symbol.id);
|
|
39699
|
-
outermostExportedName ??= destructuredPropertyName;
|
|
39700
|
-
if (destructuredPropertyName !== null) exportedPropertyDepth += getDestructuredBindingDepth(symbol.bindingIdentifier);
|
|
39701
|
-
const unwrappedInitializer = stripParenExpression(initializer);
|
|
39702
|
-
if (destructuredPropertyName === null && !isNodeOfType(unwrappedInitializer, "MemberExpression")) commonJsModuleObjectSymbols.add(symbol);
|
|
39703
|
-
expression = unwrappedInitializer;
|
|
39704
|
-
}
|
|
39705
|
-
if (!resolvedBinding) return null;
|
|
39706
|
-
if (!ignoreCommonJsMutation && isCommonJsModuleObject && !hasSafeReceiverAliases(commonJsModuleObjectSymbols, scopes)) return null;
|
|
39707
|
-
if (outermostExportedName === null) return resolvedBinding;
|
|
39708
|
-
if (!resolvedBinding.isModuleObject || exportedPropertyDepth !== 1) return null;
|
|
39709
|
-
return {
|
|
39710
|
-
source: resolvedBinding.source,
|
|
39711
|
-
exportedName: outermostExportedName,
|
|
39712
|
-
isModuleObject: false
|
|
39713
|
-
};
|
|
39714
|
-
};
|
|
39715
|
-
const getCanonicalExternalSyncExportName = (binding) => {
|
|
39716
|
-
if (binding.exportedName && binding.exportedName !== "default") return binding.exportedName;
|
|
39717
|
-
return EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.get(binding.source) ?? null;
|
|
39718
|
-
};
|
|
39719
|
-
const isKnownExternalSyncDirectModuleBinding = (binding) => {
|
|
39720
|
-
const exportedName = getCanonicalExternalSyncExportName(binding);
|
|
39721
|
-
return Boolean(exportedName && EXTERNAL_SYNC_DIRECT_IMPORT_SOURCES.get(exportedName)?.has(binding.source));
|
|
39722
|
-
};
|
|
39723
|
-
const DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES = new Set([
|
|
39724
|
-
"Array",
|
|
39725
|
-
"BigInt",
|
|
39726
|
-
"Boolean",
|
|
39727
|
-
"Date",
|
|
39728
|
-
"Number",
|
|
39729
|
-
"String",
|
|
39730
|
-
"Symbol"
|
|
39731
|
-
]);
|
|
39732
|
-
const DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES = new Set([
|
|
39733
|
-
"Array",
|
|
39734
|
-
"Boolean",
|
|
39735
|
-
"Date",
|
|
39736
|
-
"Map",
|
|
39737
|
-
"Number",
|
|
39738
|
-
"Promise",
|
|
39739
|
-
"RegExp",
|
|
39740
|
-
"Set",
|
|
39741
|
-
"String",
|
|
39742
|
-
"URL",
|
|
39743
|
-
"URLSearchParams",
|
|
39744
|
-
"WeakMap",
|
|
39745
|
-
"WeakSet"
|
|
39746
|
-
]);
|
|
39747
|
-
const PROMISE_STATIC_RESULT_METHOD_NAMES = new Set([
|
|
39748
|
-
"all",
|
|
39749
|
-
"allSettled",
|
|
39750
|
-
"any",
|
|
39751
|
-
"race",
|
|
39752
|
-
"reject",
|
|
39753
|
-
"resolve",
|
|
39754
|
-
"withResolvers"
|
|
39755
|
-
]);
|
|
39756
|
-
const returnsOnlyNonCleanupValues = (functionNode, setterToStateName, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
|
|
39757
|
-
if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return false;
|
|
39758
|
-
const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
|
|
39759
|
-
const isNonCleanupValue = (returnValue) => {
|
|
39760
|
-
const expression = stripParenExpression(returnValue);
|
|
39761
|
-
if (isNodeOfType(expression, "Identifier") && expression.name === "undefined" && scopes.isGlobalReference(expression)) return true;
|
|
39762
|
-
if (isNodeOfType(expression, "Literal") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "JSXElement") || isNodeOfType(expression, "JSXFragment") || isNodeOfType(expression, "TemplateLiteral") || isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return true;
|
|
39763
|
-
if (isNodeOfType(expression, "ConditionalExpression")) return isNonCleanupValue(expression.consequent) && isNonCleanupValue(expression.alternate);
|
|
39764
|
-
if (isNodeOfType(expression, "LogicalExpression")) return isNonCleanupValue(expression.left) && isNonCleanupValue(expression.right);
|
|
39765
|
-
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
39766
|
-
const finalExpression = expression.expressions.at(-1);
|
|
39767
|
-
return Boolean(finalExpression && isNonCleanupValue(finalExpression));
|
|
39768
|
-
}
|
|
39769
|
-
if (isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
|
|
39770
|
-
if (!isNodeOfType(expression, "CallExpression")) return false;
|
|
39771
|
-
if (isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
|
|
39772
|
-
if (isNodeOfType(expression.callee, "MemberExpression") && isNodeOfType(expression.callee.object, "Identifier") && expression.callee.object.name === "Promise" && scopes.isGlobalReference(expression.callee.object) && PROMISE_STATIC_RESULT_METHOD_NAMES.has(getStaticPropertyName(expression.callee) ?? "")) return true;
|
|
39773
|
-
if (isNodeOfType(expression.callee, "Identifier") && setterToStateName.has(expression.callee.name)) return true;
|
|
39774
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
|
|
39775
|
-
return Boolean(invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes, nextVisitedFunctions));
|
|
39776
|
-
};
|
|
39777
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNonCleanupValue(functionNode.body);
|
|
39778
|
-
let returnsOnlyNonCleanup = true;
|
|
39779
|
-
walkAst(functionNode.body, (child) => {
|
|
39780
|
-
if (!returnsOnlyNonCleanup) return false;
|
|
39781
|
-
if (child !== functionNode.body && isFunctionLike$1(child)) return false;
|
|
39782
|
-
if (!isNodeOfType(child, "ReturnStatement")) return;
|
|
39783
|
-
if (child.argument && !isNonCleanupValue(child.argument)) {
|
|
39784
|
-
returnsOnlyNonCleanup = false;
|
|
39785
|
-
return false;
|
|
39786
|
-
}
|
|
39787
|
-
});
|
|
39788
|
-
return returnsOnlyNonCleanup;
|
|
39789
|
-
};
|
|
39790
|
-
const getResolvedFunctionCleanupReturnProof = (functionNode, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
|
|
39791
|
-
if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return {
|
|
39792
|
-
hasCleanup: false,
|
|
39793
|
-
isValid: false
|
|
39794
|
-
};
|
|
39795
|
-
const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
|
|
39796
|
-
const getCleanupReturnProof = (returnValue) => {
|
|
39797
|
-
const expression = stripParenExpression(returnValue);
|
|
39798
|
-
if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression")) return {
|
|
39799
|
-
hasCleanup: true,
|
|
39800
|
-
isValid: true
|
|
39801
|
-
};
|
|
39802
|
-
if (isNodeOfType(expression, "Identifier")) {
|
|
39803
|
-
if (expression.name === "undefined" && scopes.isGlobalReference(expression)) return {
|
|
39804
|
-
hasCleanup: false,
|
|
39805
|
-
isValid: true
|
|
39806
|
-
};
|
|
39807
|
-
const localFunction = resolveExactLocalFunction(expression, scopes);
|
|
39808
|
-
return {
|
|
39809
|
-
hasCleanup: Boolean(localFunction),
|
|
39810
|
-
isValid: Boolean(localFunction)
|
|
39811
|
-
};
|
|
39812
|
-
}
|
|
39813
|
-
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return {
|
|
39814
|
-
hasCleanup: false,
|
|
39815
|
-
isValid: true
|
|
39816
|
-
};
|
|
39817
|
-
if (isNodeOfType(expression, "ConditionalExpression")) {
|
|
39818
|
-
const consequentProof = getCleanupReturnProof(expression.consequent);
|
|
39819
|
-
const alternateProof = getCleanupReturnProof(expression.alternate);
|
|
39820
|
-
return {
|
|
39821
|
-
hasCleanup: consequentProof.hasCleanup || alternateProof.hasCleanup,
|
|
39822
|
-
isValid: consequentProof.isValid && alternateProof.isValid
|
|
39823
|
-
};
|
|
39824
|
-
}
|
|
39825
|
-
if (isNodeOfType(expression, "LogicalExpression")) {
|
|
39826
|
-
const leftProof = getCleanupReturnProof(expression.left);
|
|
39827
|
-
const rightProof = getCleanupReturnProof(expression.right);
|
|
39828
|
-
return {
|
|
39829
|
-
hasCleanup: leftProof.hasCleanup || rightProof.hasCleanup,
|
|
39830
|
-
isValid: leftProof.isValid && rightProof.isValid
|
|
39831
|
-
};
|
|
39832
|
-
}
|
|
39833
|
-
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
39834
|
-
const finalExpression = expression.expressions.at(-1);
|
|
39835
|
-
return finalExpression ? getCleanupReturnProof(finalExpression) : {
|
|
39836
|
-
hasCleanup: false,
|
|
39837
|
-
isValid: false
|
|
39838
|
-
};
|
|
39839
|
-
}
|
|
39840
|
-
if (!isNodeOfType(expression, "CallExpression")) return {
|
|
39841
|
-
hasCleanup: false,
|
|
39842
|
-
isValid: false
|
|
39843
|
-
};
|
|
39844
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
|
|
39845
|
-
return invokedFunction ? getResolvedFunctionCleanupReturnProof(invokedFunction, scopes, nextVisitedFunctions) : {
|
|
39846
|
-
hasCleanup: false,
|
|
39847
|
-
isValid: false
|
|
39848
|
-
};
|
|
39849
|
-
};
|
|
39850
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return getCleanupReturnProof(functionNode.body);
|
|
39851
|
-
let cleanupReturnProof = {
|
|
39852
|
-
hasCleanup: false,
|
|
39853
|
-
isValid: true
|
|
39854
|
-
};
|
|
39855
|
-
walkAst(functionNode.body, (child) => {
|
|
39856
|
-
if (!cleanupReturnProof.isValid) return false;
|
|
39857
|
-
if (child !== functionNode.body && isFunctionLike$1(child)) return false;
|
|
39858
|
-
if (!isNodeOfType(child, "ReturnStatement")) return;
|
|
39859
|
-
if (!child.argument) return;
|
|
39860
|
-
const returnProof = getCleanupReturnProof(child.argument);
|
|
39861
|
-
if (!returnProof.isValid) {
|
|
39862
|
-
cleanupReturnProof = {
|
|
39863
|
-
hasCleanup: false,
|
|
39864
|
-
isValid: false
|
|
39865
|
-
};
|
|
39866
|
-
return false;
|
|
39867
|
-
}
|
|
39868
|
-
cleanupReturnProof.hasCleanup ||= returnProof.hasCleanup;
|
|
39869
|
-
});
|
|
39870
|
-
return cleanupReturnProof;
|
|
39871
|
-
};
|
|
39872
|
-
const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
|
|
39873
|
-
const unwrappedReturnedValue = stripParenExpression(returnedValue);
|
|
39874
|
-
if (isNodeOfType(unwrappedReturnedValue, "ArrowFunctionExpression") || isNodeOfType(unwrappedReturnedValue, "FunctionExpression")) return true;
|
|
39875
|
-
if (isNodeOfType(unwrappedReturnedValue, "CallExpression")) {
|
|
39876
|
-
if (isNodeOfType(unwrappedReturnedValue.callee, "Identifier")) {
|
|
39877
|
-
const setterSymbol = resolveConstIdentifierAlias(unwrappedReturnedValue.callee, scopes, true);
|
|
39878
|
-
if (setterToStateName.has(unwrappedReturnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
|
|
39879
|
-
}
|
|
39880
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(unwrappedReturnedValue.callee, scopes);
|
|
39881
|
-
if (invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes)) return false;
|
|
39882
|
-
if (invokedFunction) {
|
|
39883
|
-
const cleanupReturnProof = getResolvedFunctionCleanupReturnProof(invokedFunction, scopes);
|
|
39884
|
-
if (cleanupReturnProof.isValid) return cleanupReturnProof.hasCleanup;
|
|
39885
|
-
}
|
|
39886
|
-
if (!invokedFunction && isNodeOfType(unwrappedReturnedValue.callee, "Identifier") && isSetterIdentifier(unwrappedReturnedValue.callee.name)) return true;
|
|
39887
|
-
return isCleanupReturn(unwrappedReturnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
|
|
39888
|
-
}
|
|
39889
|
-
if (isNodeOfType(unwrappedReturnedValue, "Identifier")) return true;
|
|
38905
|
+
const STORAGE_GLOBAL_NAMES = new Set(["localStorage", "sessionStorage"]);
|
|
38906
|
+
const isBrowserStorageReceiver = (receiver) => {
|
|
38907
|
+
if (!receiver) return false;
|
|
38908
|
+
if (isNodeOfType(receiver, "Identifier")) return STORAGE_GLOBAL_NAMES.has(receiver.name);
|
|
38909
|
+
if (isNodeOfType(receiver, "MemberExpression")) return isNodeOfType(receiver.property, "Identifier") && STORAGE_GLOBAL_NAMES.has(receiver.property.name);
|
|
39890
38910
|
return false;
|
|
39891
38911
|
};
|
|
39892
|
-
const STORAGE_GLOBAL_NAMES = ["localStorage", "sessionStorage"];
|
|
39893
|
-
const isBrowserStorageReceiver = (receiver, scopes) => Boolean(receiver && STORAGE_GLOBAL_NAMES.some((storageName) => isProvenGlobalNamespaceReference(receiver, storageName, scopes)));
|
|
39894
38912
|
const STORAGE_HOOK_PATTERN = /^use\w*Storage/i;
|
|
39895
38913
|
const collectStorageHookSetterNames = (componentBody) => {
|
|
39896
38914
|
const setterNames = /* @__PURE__ */ new Set();
|
|
@@ -39922,16 +38940,23 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
|
|
|
39922
38940
|
});
|
|
39923
38941
|
return didFindOpaqueSetterCall;
|
|
39924
38942
|
};
|
|
39925
|
-
const
|
|
38943
|
+
const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
|
|
39926
38944
|
allowGlobalReactNamespace: true,
|
|
39927
38945
|
allowUnboundBareCalls: true,
|
|
39928
38946
|
resolveNamedAliases: true
|
|
39929
|
-
})
|
|
39930
|
-
const isReactRefCall = (expression, scopes) => isReactUseRefCall(expression, scopes) || isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "createRef", scopes, {
|
|
38947
|
+
}) || isReactApiCall(expression, "createRef", scopes, {
|
|
39931
38948
|
allowGlobalReactNamespace: true,
|
|
39932
38949
|
allowUnboundBareCalls: true,
|
|
39933
38950
|
resolveNamedAliases: true
|
|
39934
|
-
});
|
|
38951
|
+
}));
|
|
38952
|
+
const getDirectReactRefSymbol = (rawExpression, scopes) => {
|
|
38953
|
+
const expression = stripParenExpression(rawExpression);
|
|
38954
|
+
if (!isNodeOfType(expression, "Identifier")) return null;
|
|
38955
|
+
const symbol = scopes.symbolFor(expression);
|
|
38956
|
+
if (!symbol) return null;
|
|
38957
|
+
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
38958
|
+
return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
|
|
38959
|
+
};
|
|
39935
38960
|
const isReactNativeJsxElement = (openingElement, scopes) => {
|
|
39936
38961
|
if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
|
|
39937
38962
|
const symbol = scopes.symbolFor(openingElement.name);
|
|
@@ -39939,33 +38964,19 @@ const isReactNativeJsxElement = (openingElement, scopes) => {
|
|
|
39939
38964
|
return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
|
|
39940
38965
|
};
|
|
39941
38966
|
const isDirectHostJsxRef = (symbol, scopes) => {
|
|
39942
|
-
let
|
|
39943
|
-
const
|
|
39944
|
-
|
|
39945
|
-
|
|
39946
|
-
|
|
39947
|
-
if (!
|
|
39948
|
-
|
|
39949
|
-
|
|
39950
|
-
|
|
39951
|
-
|
|
39952
|
-
|
|
39953
|
-
|
|
39954
|
-
|
|
39955
|
-
const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
|
|
39956
|
-
if (!aliasSymbol || aliasInitializer !== expression) return false;
|
|
39957
|
-
pendingSymbols.push(aliasSymbol);
|
|
39958
|
-
continue;
|
|
39959
|
-
}
|
|
39960
|
-
if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
|
|
39961
|
-
const attribute = container.parent;
|
|
39962
|
-
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
39963
|
-
const openingElement = attribute.parent;
|
|
39964
|
-
if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
|
|
39965
|
-
didFindHostRef = true;
|
|
39966
|
-
}
|
|
39967
|
-
}
|
|
39968
|
-
return didFindHostRef;
|
|
38967
|
+
let hostRefCount = 0;
|
|
38968
|
+
for (const reference of symbol.references) {
|
|
38969
|
+
const expression = findTransparentExpressionRoot(reference.identifier);
|
|
38970
|
+
const container = expression.parent;
|
|
38971
|
+
if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
|
|
38972
|
+
if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
|
|
38973
|
+
const attribute = container.parent;
|
|
38974
|
+
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
38975
|
+
const openingElement = attribute.parent;
|
|
38976
|
+
if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
|
|
38977
|
+
hostRefCount += 1;
|
|
38978
|
+
}
|
|
38979
|
+
return hostRefCount > 0;
|
|
39969
38980
|
};
|
|
39970
38981
|
const isIntrinsicRefCallbackParameter = (expression, scopes) => {
|
|
39971
38982
|
const identifier = stripParenExpression(expression);
|
|
@@ -39975,18 +38986,7 @@ const isIntrinsicRefCallbackParameter = (expression, scopes) => {
|
|
|
39975
38986
|
const rawFirstParameter = callback.params?.[0];
|
|
39976
38987
|
const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
|
|
39977
38988
|
const symbol = scopes.symbolFor(identifier);
|
|
39978
|
-
return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter
|
|
39979
|
-
if (reference.flag !== "read") return false;
|
|
39980
|
-
let referenceRoot = reference.identifier;
|
|
39981
|
-
while (referenceRoot.parent) {
|
|
39982
|
-
const parent = referenceRoot.parent;
|
|
39983
|
-
if (isNodeOfType(parent, "AssignmentExpression")) return parent.left !== referenceRoot;
|
|
39984
|
-
if (isNodeOfType(parent, "UpdateExpression")) return parent.argument !== referenceRoot;
|
|
39985
|
-
if (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) return parent.left !== referenceRoot;
|
|
39986
|
-
referenceRoot = parent;
|
|
39987
|
-
}
|
|
39988
|
-
return true;
|
|
39989
|
-
}));
|
|
38989
|
+
return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
|
|
39990
38990
|
};
|
|
39991
38991
|
const getDirectReactRefCall = (symbol, scopes) => {
|
|
39992
38992
|
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
@@ -39994,38 +38994,15 @@ const getDirectReactRefCall = (symbol, scopes) => {
|
|
|
39994
38994
|
const expression = stripParenExpression(initializer);
|
|
39995
38995
|
return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
|
|
39996
38996
|
};
|
|
39997
|
-
const isEmptyGlobalMapConstruction = (rawExpression, scopes) => {
|
|
39998
|
-
const expression = stripParenExpression(rawExpression);
|
|
39999
|
-
return isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Map" && scopes.isGlobalReference(expression.callee) && expression.arguments.length === 0;
|
|
40000
|
-
};
|
|
40001
|
-
const hasEmptyRefSentinelInitializer = (refCall, scopes) => {
|
|
40002
|
-
if (!isReactUseRefCall(refCall, scopes) || refCall.arguments.length > 1) return false;
|
|
40003
|
-
const [initialValue] = refCall.arguments;
|
|
40004
|
-
return !initialValue || isNodeOfType(initialValue, "Literal") && initialValue.value === null || isNodeOfType(initialValue, "Identifier") && initialValue.name === "undefined" && scopes.isGlobalReference(initialValue);
|
|
40005
|
-
};
|
|
40006
|
-
const isDirectLazyEmptyMapInitialization = (currentExpression, symbol, scopes) => {
|
|
40007
|
-
const assignment = currentExpression.parent;
|
|
40008
|
-
if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== currentExpression || assignment.operator !== "??=" || !isEmptyGlobalMapConstruction(assignment.right, scopes)) return false;
|
|
40009
|
-
const refOwner = findEnclosingFunction$1(symbol.bindingIdentifier);
|
|
40010
|
-
return refOwner !== null && findEnclosingFunction$1(assignment) === refOwner;
|
|
40011
|
-
};
|
|
40012
38997
|
const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
|
|
40013
|
-
const
|
|
40014
|
-
|
|
40015
|
-
const hasDirectEmptyMapInitializer = Boolean(initialValue && isEmptyGlobalMapConstruction(initialValue, scopes));
|
|
40016
|
-
const hasLazyEmptyMapInitializer = Boolean(refCall && hasEmptyRefSentinelInitializer(refCall, scopes));
|
|
40017
|
-
if (!hasDirectEmptyMapInitializer && !hasLazyEmptyMapInitializer) return false;
|
|
38998
|
+
const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
|
|
38999
|
+
if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
|
|
40018
39000
|
let intrinsicValueWriteCount = 0;
|
|
40019
|
-
let lazyEmptyMapInitializationCount = 0;
|
|
40020
39001
|
for (const reference of symbol.references) {
|
|
40021
39002
|
const identifier = findTransparentExpressionRoot(reference.identifier);
|
|
40022
39003
|
const currentMember = identifier.parent;
|
|
40023
39004
|
if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
|
|
40024
39005
|
const currentExpression = findTransparentExpressionRoot(currentMember);
|
|
40025
|
-
if (hasLazyEmptyMapInitializer && isDirectLazyEmptyMapInitialization(currentExpression, symbol, scopes)) {
|
|
40026
|
-
lazyEmptyMapInitializationCount += 1;
|
|
40027
|
-
continue;
|
|
40028
|
-
}
|
|
40029
39006
|
const methodMember = currentExpression.parent;
|
|
40030
39007
|
if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
|
|
40031
39008
|
const methodName = getStaticPropertyName(methodMember);
|
|
@@ -40038,7 +39015,7 @@ const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
|
|
|
40038
39015
|
if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
|
|
40039
39016
|
intrinsicValueWriteCount += 1;
|
|
40040
39017
|
}
|
|
40041
|
-
return intrinsicValueWriteCount > 0
|
|
39018
|
+
return intrinsicValueWriteCount > 0;
|
|
40042
39019
|
};
|
|
40043
39020
|
const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40044
39021
|
const expression = stripParenExpression(rawExpression);
|
|
@@ -40052,11 +39029,7 @@ const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollecti
|
|
|
40052
39029
|
}
|
|
40053
39030
|
if (isNodeOfType(expression, "MemberExpression")) {
|
|
40054
39031
|
if (getStaticPropertyName(expression) === "current") {
|
|
40055
|
-
const symbol =
|
|
40056
|
-
allowUnboundBareCalls: true,
|
|
40057
|
-
includeCreateRef: true,
|
|
40058
|
-
resolveNamedAliases: true
|
|
40059
|
-
});
|
|
39032
|
+
const symbol = getDirectReactRefSymbol(expression.object, scopes);
|
|
40060
39033
|
return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
|
|
40061
39034
|
}
|
|
40062
39035
|
return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
|
|
@@ -40074,80 +39047,19 @@ const isCommittedDomSyncNode = (node, scopes) => {
|
|
|
40074
39047
|
if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
|
|
40075
39048
|
return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
|
|
40076
39049
|
};
|
|
40077
|
-
const
|
|
40078
|
-
|
|
40079
|
-
if (isNodeOfType(
|
|
40080
|
-
const receiverSymbol = scopes.symbolFor(expression);
|
|
40081
|
-
if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
|
|
40082
|
-
}
|
|
40083
|
-
if (hasProvenGlobalNamespaceReference(expression, EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES, scopes)) return true;
|
|
40084
|
-
const moduleBinding = resolveExternalModuleBinding(expression, scopes);
|
|
40085
|
-
if (moduleBinding && EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES.has(moduleBinding.source) && (moduleBinding.isModuleObject || moduleBinding.exportedName === "default" && EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.has(moduleBinding.source) || isKnownExternalSyncDirectModuleBinding(moduleBinding))) return true;
|
|
40086
|
-
if (isNodeOfType(expression, "CallExpression")) {
|
|
40087
|
-
const callee = stripParenExpression(expression.callee);
|
|
40088
|
-
return Boolean(isNodeOfType(callee, "MemberExpression") && getStaticPropertyName(callee) === "create" && isProvenHttpClientReceiver(callee.object, scopes, visitedSymbolIds));
|
|
40089
|
-
}
|
|
40090
|
-
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
40091
|
-
const symbol = scopes.symbolFor(expression);
|
|
40092
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
40093
|
-
if (resolveExternalModuleBinding(expression, scopes, /* @__PURE__ */ new Set(), true)) return false;
|
|
40094
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
40095
|
-
return Boolean(initializer && isProvenHttpClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
|
|
40096
|
-
};
|
|
40097
|
-
const isProvenTanStackQueryClientReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40098
|
-
const expression = stripParenExpression(rawExpression);
|
|
40099
|
-
if (isNodeOfType(expression, "Identifier")) {
|
|
40100
|
-
const receiverSymbol = scopes.symbolFor(expression);
|
|
40101
|
-
if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
|
|
40102
|
-
}
|
|
40103
|
-
if (isNodeOfType(expression, "CallExpression")) {
|
|
40104
|
-
const binding = resolveExternalModuleBinding(expression.callee, scopes);
|
|
40105
|
-
return Boolean(binding && binding.source === "@tanstack/react-query" && binding.exportedName === "useQueryClient");
|
|
40106
|
-
}
|
|
40107
|
-
if (isNodeOfType(expression, "NewExpression")) {
|
|
40108
|
-
const binding = resolveExternalModuleBinding(expression.callee, scopes);
|
|
40109
|
-
return Boolean(binding && TANSTACK_QUERY_CLIENT_MODULE_SOURCES.has(binding.source) && binding.exportedName === "QueryClient");
|
|
40110
|
-
}
|
|
40111
|
-
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
40112
|
-
const symbol = scopes.symbolFor(expression);
|
|
40113
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
40114
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
40115
|
-
return Boolean(initializer && isProvenTanStackQueryClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
|
|
40116
|
-
};
|
|
40117
|
-
const isProvenExternalResourceReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40118
|
-
const expression = stripParenExpression(rawExpression);
|
|
40119
|
-
if (isDerivedFromProvenDomRefCurrent(expression, scopes) || isProvenBrowserApiReceiver(expression, "dom-event-target", scopes) || isProvenBrowserApiReceiver(expression, "xml-http-request", scopes) || isProvenHttpClientReceiver(expression, scopes)) return true;
|
|
40120
|
-
if (isNodeOfType(expression, "NewExpression")) return hasProvenGlobalNamespaceReference(expression.callee, EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES, scopes);
|
|
40121
|
-
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
40122
|
-
const symbol = scopes.symbolFor(expression);
|
|
40123
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
40124
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
40125
|
-
return Boolean(initializer && isProvenExternalResourceReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
|
|
40126
|
-
};
|
|
40127
|
-
const isProvenExternalSyncDirectCallee = (callee, scopes) => {
|
|
40128
|
-
if (hasProvenGlobalNamespaceReference(callee, EXTERNAL_SYNC_DIRECT_CALLEE_NAMES, scopes)) return true;
|
|
40129
|
-
const moduleBinding = resolveExternalModuleBinding(callee, scopes);
|
|
40130
|
-
return Boolean(moduleBinding && isKnownExternalSyncDirectModuleBinding(moduleBinding));
|
|
40131
|
-
};
|
|
40132
|
-
const isExternalSyncNode = (node, scopes) => {
|
|
40133
|
-
if (isNodeOfType(node, "NewExpression")) return hasProvenGlobalNamespaceReference(node.callee, EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS, scopes);
|
|
40134
|
-
if (isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression")) {
|
|
40135
|
-
const mutationTarget = isNodeOfType(node, "AssignmentExpression") ? node.left : node.argument;
|
|
40136
|
-
return isNodeOfType(mutationTarget, "MemberExpression") && getStaticPropertyName(mutationTarget) === "current" && Boolean(resolveReactRefSymbol(mutationTarget, scopes, {
|
|
40137
|
-
allowUnboundBareCalls: true,
|
|
40138
|
-
includeCreateRef: true,
|
|
40139
|
-
resolveNamedAliases: true
|
|
40140
|
-
}));
|
|
40141
|
-
}
|
|
39050
|
+
const isExternalSyncNode = (node) => {
|
|
39051
|
+
if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
|
|
39052
|
+
if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
|
|
40142
39053
|
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
40143
|
-
if (
|
|
39054
|
+
if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
|
|
40144
39055
|
if (!isNodeOfType(node.callee, "MemberExpression")) return false;
|
|
40145
39056
|
const propertyName = getStaticPropertyName(node.callee);
|
|
40146
39057
|
if (propertyName === null) return false;
|
|
40147
|
-
if (
|
|
40148
|
-
if (
|
|
40149
|
-
if (
|
|
40150
|
-
|
|
39058
|
+
if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
|
|
39059
|
+
if (isBrowserStorageReceiver(node.callee.object)) return true;
|
|
39060
|
+
if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
|
|
39061
|
+
const receiverRootName = getRootIdentifierName(node.callee.object);
|
|
39062
|
+
return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
|
|
40151
39063
|
};
|
|
40152
39064
|
const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, scopes, allowCommittedDomSync) => {
|
|
40153
39065
|
if (!isFunctionLike$1(effectCallback)) return false;
|
|
@@ -40156,7 +39068,7 @@ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateNa
|
|
|
40156
39068
|
} else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, setterSymbolIdToStateName, scopes, true)) return true;
|
|
40157
39069
|
let didFindExternalCall = false;
|
|
40158
39070
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
40159
|
-
if (isExternalSyncNode(child
|
|
39071
|
+
if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
|
|
40160
39072
|
});
|
|
40161
39073
|
return didFindExternalCall;
|
|
40162
39074
|
};
|
|
@@ -40178,11 +39090,11 @@ const noEffectChain = defineRule({
|
|
|
40178
39090
|
setterToStateName.set(binding.setterName, binding.valueName);
|
|
40179
39091
|
if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
|
|
40180
39092
|
const stateIdentifier = binding.declarator.id.elements[0];
|
|
40181
|
-
const setterIdentifier = binding.declarator.id.elements[1];
|
|
40182
39093
|
if (isNodeOfType(stateIdentifier, "Identifier")) {
|
|
40183
39094
|
const stateSymbol = context.scopes.symbolFor(stateIdentifier);
|
|
40184
39095
|
if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
|
|
40185
39096
|
}
|
|
39097
|
+
const setterIdentifier = binding.declarator.id.elements[1];
|
|
40186
39098
|
if (isNodeOfType(setterIdentifier, "Identifier")) {
|
|
40187
39099
|
const setterSymbol = context.scopes.symbolFor(setterIdentifier);
|
|
40188
39100
|
if (setterSymbol) setterSymbolIdToStateName.set(setterSymbol.id, binding.valueName);
|
|
@@ -40195,11 +39107,10 @@ const noEffectChain = defineRule({
|
|
|
40195
39107
|
const callback = getEffectCallback(effectCall, context.scopes);
|
|
40196
39108
|
if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
|
|
40197
39109
|
const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
|
|
40198
|
-
const stateWrites = collectStateWritesInEffect(
|
|
39110
|
+
const stateWrites = collectStateWritesInEffect(analysisFunctions, setterSymbolIdToStateName, context.scopes);
|
|
40199
39111
|
const writtenStateNames = new Set(stateWrites.keys());
|
|
40200
39112
|
effectInfos.push({
|
|
40201
39113
|
node: effectCall,
|
|
40202
|
-
callback,
|
|
40203
39114
|
dependencyStateSymbolIds: collectDependencyStateSymbolIds(effectCall, stateSymbolIdSet, context.scopes),
|
|
40204
39115
|
stateWrites,
|
|
40205
39116
|
analysisFunctions,
|
|
@@ -40216,10 +39127,10 @@ const noEffectChain = defineRule({
|
|
|
40216
39127
|
if (readerEffect.isExternalSync) continue;
|
|
40217
39128
|
if (readerEffect.dependencyStateSymbolIds.size === 0) continue;
|
|
40218
39129
|
let chainedStateName = null;
|
|
40219
|
-
for (const writtenName of writerEffect.stateWrites
|
|
39130
|
+
for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
|
|
40220
39131
|
const writtenStateSymbolId = stateSymbolIds.get(writtenName);
|
|
40221
39132
|
if (writtenStateSymbolId === void 0 || !readerEffect.dependencyStateSymbolIds.has(writtenStateSymbolId)) continue;
|
|
40222
|
-
if (!canStateWriteReachReaderWork(
|
|
39133
|
+
if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
|
|
40223
39134
|
chainedStateName = writtenName;
|
|
40224
39135
|
break;
|
|
40225
39136
|
}
|
|
@@ -45004,6 +43915,16 @@ const noManyBooleanProps = defineRule({
|
|
|
45004
43915
|
}
|
|
45005
43916
|
});
|
|
45006
43917
|
//#endregion
|
|
43918
|
+
//#region src/plugin/utils/is-global-match-media-call.ts
|
|
43919
|
+
const isGlobalMatchMediaCall = (node, scopes) => {
|
|
43920
|
+
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
43921
|
+
const callee = stripParenExpression(node.callee);
|
|
43922
|
+
if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
|
|
43923
|
+
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
|
|
43924
|
+
const receiver = stripParenExpression(callee.object);
|
|
43925
|
+
return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
|
|
43926
|
+
};
|
|
43927
|
+
//#endregion
|
|
45007
43928
|
//#region src/plugin/rules/performance/no-match-media-in-state-initializer.ts
|
|
45008
43929
|
const REACT_USE_STATE_OPTIONS = {
|
|
45009
43930
|
allowGlobalReactNamespace: true,
|
|
@@ -56529,6 +55450,17 @@ const preferModuleScopePureFunction = defineRule({
|
|
|
56529
55450
|
}
|
|
56530
55451
|
});
|
|
56531
55452
|
//#endregion
|
|
55453
|
+
//#region src/plugin/utils/get-require-call-source.ts
|
|
55454
|
+
const getRequireCallSource = (expression) => {
|
|
55455
|
+
const unwrappedExpression = stripParenExpression(expression);
|
|
55456
|
+
if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
|
|
55457
|
+
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
|
|
55458
|
+
if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
|
|
55459
|
+
const [firstArgument] = unwrappedExpression.arguments ?? [];
|
|
55460
|
+
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
55461
|
+
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
55462
|
+
};
|
|
55463
|
+
//#endregion
|
|
56532
55464
|
//#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
|
|
56533
55465
|
const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
|
|
56534
55466
|
const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
|
|
@@ -61236,717 +60168,6 @@ const isSetterCalledDuringRender = (root, setterName) => {
|
|
|
61236
60168
|
return found;
|
|
61237
60169
|
};
|
|
61238
60170
|
//#endregion
|
|
61239
|
-
//#region src/plugin/rules/state-and-effects/utils/create-external-location-invalidation-checker.ts
|
|
61240
|
-
const HISTORY_LOCATION_MUTATION_METHOD_NAMES = new Set(["pushState", "replaceState"]);
|
|
61241
|
-
const AGGREGATE_MUTATION_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
|
|
61242
|
-
const OBJECT_AGGREGATE_MUTATION_METHOD_NAMES = new Set([
|
|
61243
|
-
"assign",
|
|
61244
|
-
"defineProperties",
|
|
61245
|
-
"defineProperty",
|
|
61246
|
-
"setPrototypeOf"
|
|
61247
|
-
]);
|
|
61248
|
-
const REFLECT_AGGREGATE_MUTATION_METHOD_NAMES = new Set([
|
|
61249
|
-
"defineProperty",
|
|
61250
|
-
"deleteProperty",
|
|
61251
|
-
"set",
|
|
61252
|
-
"setPrototypeOf"
|
|
61253
|
-
]);
|
|
61254
|
-
const LOCATION_CHANGE_EVENT_NAMES = new Set(["hashchange", "popstate"]);
|
|
61255
|
-
const containsGlobalLocationSnapshotRead = (node, scopes) => {
|
|
61256
|
-
let didFindLocationSnapshotRead = false;
|
|
61257
|
-
walkAst(node, (child) => {
|
|
61258
|
-
if (didFindLocationSnapshotRead) return false;
|
|
61259
|
-
if (child !== node && isFunctionLike$1(child) && !executesDuringRender(findTransparentExpressionRoot(child), scopes)) return false;
|
|
61260
|
-
if (!isProvenGlobalNamespaceReference(child, "location", scopes)) return;
|
|
61261
|
-
didFindLocationSnapshotRead = true;
|
|
61262
|
-
return false;
|
|
61263
|
-
});
|
|
61264
|
-
return didFindLocationSnapshotRead;
|
|
61265
|
-
};
|
|
61266
|
-
const resolveExactLocalOrReactCallbackFunction = (expression, scopes) => {
|
|
61267
|
-
const localFunction = resolveExactLocalFunction(expression, scopes);
|
|
61268
|
-
if (isFunctionLike$1(localFunction)) return localFunction;
|
|
61269
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
61270
|
-
let callbackSource = unwrappedExpression;
|
|
61271
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
61272
|
-
const callbackSymbol = resolveConstIdentifierAlias(unwrappedExpression, scopes);
|
|
61273
|
-
if (callbackSymbol?.kind !== "const" || !callbackSymbol.initializer) return null;
|
|
61274
|
-
callbackSource = callbackSymbol.initializer;
|
|
61275
|
-
}
|
|
61276
|
-
const unwrappedCallbackSource = stripParenExpression(callbackSource);
|
|
61277
|
-
if (!isNodeOfType(unwrappedCallbackSource, "CallExpression") || !isReactApiCall(unwrappedCallbackSource, "useCallback", scopes, { resolveNamedAliases: true })) return null;
|
|
61278
|
-
const callback = unwrappedCallbackSource.arguments?.[0];
|
|
61279
|
-
if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
|
|
61280
|
-
return resolveExactLocalFunction(stripParenExpression(callback), scopes);
|
|
61281
|
-
};
|
|
61282
|
-
const bindingReadsExactGlobalLocationSnapshot = (bindingIdentifier, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
61283
|
-
const symbol = scopes.symbolFor(bindingIdentifier);
|
|
61284
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
61285
|
-
if ((symbol.kind === "function" ? scopes.scopeFor(symbol.declarationNode) : symbol.scope).symbols.some((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.references.some((reference) => reference.flag !== "read"))) return false;
|
|
61286
|
-
const resolvedFunction = resolveExactLocalOrReactCallbackFunction(bindingIdentifier, scopes);
|
|
61287
|
-
const initializer = isFunctionLike$1(resolvedFunction) ? resolvedFunction.body : getDirectUnreassignedInitializer(symbol);
|
|
61288
|
-
if (!initializer) return false;
|
|
61289
|
-
const nextVisitedSymbolIds = new Set(visitedSymbolIds);
|
|
61290
|
-
nextVisitedSymbolIds.add(symbol.id);
|
|
61291
|
-
if (containsGlobalLocationSnapshotRead(initializer, scopes)) return true;
|
|
61292
|
-
let didFindAliasedLocationSnapshotRead = false;
|
|
61293
|
-
walkAst(initializer, (child) => {
|
|
61294
|
-
if (didFindAliasedLocationSnapshotRead) return false;
|
|
61295
|
-
if (child !== initializer && isFunctionLike$1(child) && !executesDuringRender(findTransparentExpressionRoot(child), scopes)) return false;
|
|
61296
|
-
if (!isNodeOfType(child, "Identifier")) return;
|
|
61297
|
-
if (bindingReadsExactGlobalLocationSnapshot(child, scopes, nextVisitedSymbolIds)) {
|
|
61298
|
-
didFindAliasedLocationSnapshotRead = true;
|
|
61299
|
-
return false;
|
|
61300
|
-
}
|
|
61301
|
-
});
|
|
61302
|
-
return didFindAliasedLocationSnapshotRead;
|
|
61303
|
-
};
|
|
61304
|
-
const hasRenderReachableLocationSnapshotRead = (componentBody, renderReachableExpressions, directRenderNames, scopes) => {
|
|
61305
|
-
if (renderReachableExpressions.some((expression) => containsGlobalLocationSnapshotRead(expression, scopes))) return true;
|
|
61306
|
-
for (const statement of componentBody.body ?? []) {
|
|
61307
|
-
if (isNodeOfType(statement, "FunctionDeclaration") && statement.id) {
|
|
61308
|
-
if (directRenderNames.has(statement.id.name) && bindingReadsExactGlobalLocationSnapshot(statement.id, scopes)) return true;
|
|
61309
|
-
continue;
|
|
61310
|
-
}
|
|
61311
|
-
if (!isNodeOfType(statement, "VariableDeclaration")) continue;
|
|
61312
|
-
for (const declarator of statement.declarations ?? []) {
|
|
61313
|
-
if (!isNodeOfType(declarator.id, "Identifier") || !declarator.init) continue;
|
|
61314
|
-
if (!directRenderNames.has(declarator.id.name)) continue;
|
|
61315
|
-
if (bindingReadsExactGlobalLocationSnapshot(declarator.id, scopes)) return true;
|
|
61316
|
-
}
|
|
61317
|
-
}
|
|
61318
|
-
return false;
|
|
61319
|
-
};
|
|
61320
|
-
const isGlobalHistoryLocationMutation = (node, scopes) => {
|
|
61321
|
-
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
61322
|
-
const callee = stripParenExpression(node.callee);
|
|
61323
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
61324
|
-
const methodName = getStaticPropertyName(callee);
|
|
61325
|
-
return Boolean(methodName && HISTORY_LOCATION_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "history", scopes));
|
|
61326
|
-
};
|
|
61327
|
-
const getStaticLocationChangeEvent = (node) => {
|
|
61328
|
-
if (!node) return null;
|
|
61329
|
-
const eventNameNode = stripParenExpression(node);
|
|
61330
|
-
if (!isNodeOfType(eventNameNode, "Literal") || typeof eventNameNode.value !== "string") return null;
|
|
61331
|
-
return LOCATION_CHANGE_EVENT_NAMES.has(eventNameNode.value) ? eventNameNode.value : null;
|
|
61332
|
-
};
|
|
61333
|
-
const addToSetIndex = (index, key, value) => {
|
|
61334
|
-
const indexedValues = index.get(key) ?? /* @__PURE__ */ new Set();
|
|
61335
|
-
indexedValues.add(value);
|
|
61336
|
-
index.set(key, indexedValues);
|
|
61337
|
-
};
|
|
61338
|
-
const getSynchronousInvocationExpression = (functionNode, scopes) => {
|
|
61339
|
-
const functionExpressionRoot = findTransparentExpressionRoot(functionNode);
|
|
61340
|
-
if (!executesDuringRender(functionExpressionRoot, scopes)) return null;
|
|
61341
|
-
const parent = functionExpressionRoot.parent;
|
|
61342
|
-
return isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression") ? parent : null;
|
|
61343
|
-
};
|
|
61344
|
-
const getLocationListenerOperation = (callExpression, scopes) => {
|
|
61345
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return null;
|
|
61346
|
-
const callee = stripParenExpression(callExpression.callee);
|
|
61347
|
-
const methodName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : isNodeOfType(callee, "Identifier") ? callee.name : null;
|
|
61348
|
-
if (methodName !== "addEventListener" && methodName !== "removeEventListener") return null;
|
|
61349
|
-
if (!(isNodeOfType(callee, "MemberExpression") ? isProvenGlobalObjectReference(callee.object, scopes) : isNodeOfType(callee, "Identifier") && scopes.isGlobalReference(callee))) return null;
|
|
61350
|
-
const eventName = getStaticLocationChangeEvent(callExpression.arguments?.[0]);
|
|
61351
|
-
if (!eventName) return null;
|
|
61352
|
-
const listenerExpression = callExpression.arguments?.[1];
|
|
61353
|
-
if (!listenerExpression || isNodeOfType(listenerExpression, "SpreadElement")) return null;
|
|
61354
|
-
const listenerFunction = resolveExactLocalOrReactCallbackFunction(listenerExpression, scopes);
|
|
61355
|
-
if (!isFunctionLike$1(listenerFunction)) return null;
|
|
61356
|
-
const captureArgument = callExpression.arguments?.[2];
|
|
61357
|
-
const capture = isNodeOfType(captureArgument, "SpreadElement") ? null : resolveEventListenerCapture(captureArgument, {
|
|
61358
|
-
allowComputedString: true,
|
|
61359
|
-
allowIndeterminateEntries: true
|
|
61360
|
-
});
|
|
61361
|
-
return {
|
|
61362
|
-
operation: methodName === "addEventListener" ? "add" : "remove",
|
|
61363
|
-
registration: {
|
|
61364
|
-
callExpression,
|
|
61365
|
-
listenerFunction,
|
|
61366
|
-
capture,
|
|
61367
|
-
eventName
|
|
61368
|
-
}
|
|
61369
|
-
};
|
|
61370
|
-
};
|
|
61371
|
-
const buildLocationInvalidationIndex = (componentBody, componentFunction, context) => {
|
|
61372
|
-
const index = {
|
|
61373
|
-
componentFunction,
|
|
61374
|
-
context,
|
|
61375
|
-
effectCallbacks: /* @__PURE__ */ new Set(),
|
|
61376
|
-
expressionsByOwner: /* @__PURE__ */ new Map(),
|
|
61377
|
-
historyMutationsByOwner: /* @__PURE__ */ new Map(),
|
|
61378
|
-
awaitExpressionsByOwner: /* @__PURE__ */ new Map(),
|
|
61379
|
-
callSitesByFunction: /* @__PURE__ */ new Map(),
|
|
61380
|
-
calledFunctionByExpression: /* @__PURE__ */ new Map(),
|
|
61381
|
-
synchronousInvocationsByFunction: /* @__PURE__ */ new Map(),
|
|
61382
|
-
synchronousCallbacksByExpression: /* @__PURE__ */ new Map(),
|
|
61383
|
-
callsByCalleeSymbolId: /* @__PURE__ */ new Map(),
|
|
61384
|
-
identifierCalls: /* @__PURE__ */ new Set(),
|
|
61385
|
-
listenerRegistrations: [],
|
|
61386
|
-
listenerRemovals: [],
|
|
61387
|
-
mountedListenerFunctions: /* @__PURE__ */ new Set(),
|
|
61388
|
-
mutationExecutionsByOwner: /* @__PURE__ */ new Map(),
|
|
61389
|
-
synchronousMutationResultByFunction: /* @__PURE__ */ new Map()
|
|
61390
|
-
};
|
|
61391
|
-
walkAst(componentBody, (child) => {
|
|
61392
|
-
if (isFunctionLike$1(child)) {
|
|
61393
|
-
const invocationExpression = getSynchronousInvocationExpression(child, context.scopes);
|
|
61394
|
-
if (invocationExpression) {
|
|
61395
|
-
addToSetIndex(index.synchronousInvocationsByFunction, child, invocationExpression);
|
|
61396
|
-
addToSetIndex(index.synchronousCallbacksByExpression, invocationExpression, child);
|
|
61397
|
-
}
|
|
61398
|
-
return;
|
|
61399
|
-
}
|
|
61400
|
-
const owner = context.cfg.enclosingFunction(child);
|
|
61401
|
-
if (!owner) return;
|
|
61402
|
-
if (isNodeOfType(child, "AwaitExpression")) {
|
|
61403
|
-
addToSetIndex(index.awaitExpressionsByOwner, owner, child);
|
|
61404
|
-
return;
|
|
61405
|
-
}
|
|
61406
|
-
if (!isNodeOfType(child, "CallExpression") && !isNodeOfType(child, "NewExpression")) return;
|
|
61407
|
-
addToSetIndex(index.expressionsByOwner, owner, child);
|
|
61408
|
-
if (isNodeOfType(child, "CallExpression")) {
|
|
61409
|
-
if (isGlobalHistoryLocationMutation(child, context.scopes)) addToSetIndex(index.historyMutationsByOwner, owner, child);
|
|
61410
|
-
if (isNodeOfType(stripParenExpression(child.callee), "Identifier")) index.identifierCalls.add(child);
|
|
61411
|
-
const calledFunction = resolveExactLocalOrReactCallbackFunction(child.callee, context.scopes);
|
|
61412
|
-
if (isFunctionLike$1(calledFunction)) {
|
|
61413
|
-
addToSetIndex(index.callSitesByFunction, calledFunction, child);
|
|
61414
|
-
index.calledFunctionByExpression.set(child, calledFunction);
|
|
61415
|
-
}
|
|
61416
|
-
if (context.cfg.enclosingFunction(child) === componentFunction && isReactApiCall(child, EFFECT_HOOK_NAMES$1, context.scopes, {
|
|
61417
|
-
allowGlobalReactNamespace: true,
|
|
61418
|
-
allowUnboundBareCalls: true,
|
|
61419
|
-
resolveNamedAliases: true
|
|
61420
|
-
})) {
|
|
61421
|
-
const effectCallback = getEffectCallback(child, context.scopes);
|
|
61422
|
-
if (isFunctionLike$1(effectCallback)) index.effectCallbacks.add(effectCallback);
|
|
61423
|
-
}
|
|
61424
|
-
const listenerOperation = getLocationListenerOperation(child, context.scopes);
|
|
61425
|
-
if (listenerOperation?.operation === "add") index.listenerRegistrations.push(listenerOperation.registration);
|
|
61426
|
-
else if (listenerOperation) index.listenerRemovals.push(listenerOperation.registration);
|
|
61427
|
-
}
|
|
61428
|
-
});
|
|
61429
|
-
return index;
|
|
61430
|
-
};
|
|
61431
|
-
const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
|
|
61432
|
-
let current = descendant;
|
|
61433
|
-
while (current && current !== ancestor) {
|
|
61434
|
-
if (current !== descendant && isFunctionLike$1(current)) return false;
|
|
61435
|
-
current = current.parent;
|
|
61436
|
-
}
|
|
61437
|
-
return current === ancestor;
|
|
61438
|
-
};
|
|
61439
|
-
const areInMutuallyExclusiveConditionalBranches = (firstNode, secondNode) => {
|
|
61440
|
-
const firstBranches = /* @__PURE__ */ new Map();
|
|
61441
|
-
let current = firstNode;
|
|
61442
|
-
while (current?.parent) {
|
|
61443
|
-
const parent = current.parent;
|
|
61444
|
-
if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current)) firstBranches.set(parent, current);
|
|
61445
|
-
if (current !== firstNode && isFunctionLike$1(current)) break;
|
|
61446
|
-
current = parent;
|
|
61447
|
-
}
|
|
61448
|
-
current = secondNode;
|
|
61449
|
-
while (current?.parent) {
|
|
61450
|
-
const parent = current.parent;
|
|
61451
|
-
if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current)) {
|
|
61452
|
-
const firstBranch = firstBranches.get(parent);
|
|
61453
|
-
if (firstBranch && firstBranch !== current) return true;
|
|
61454
|
-
}
|
|
61455
|
-
if (current !== secondNode && isFunctionLike$1(current)) break;
|
|
61456
|
-
current = parent;
|
|
61457
|
-
}
|
|
61458
|
-
return false;
|
|
61459
|
-
};
|
|
61460
|
-
const collectStableBooleanGuardConstraints = (node, functionBoundary, scopes) => {
|
|
61461
|
-
const constraintsBySymbolId = /* @__PURE__ */ new Map();
|
|
61462
|
-
const recordConstraint = (test, requiredTestValue) => {
|
|
61463
|
-
let expression = stripParenExpression(test);
|
|
61464
|
-
let requiredValue = requiredTestValue;
|
|
61465
|
-
while (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
|
|
61466
|
-
requiredValue = !requiredValue;
|
|
61467
|
-
expression = stripParenExpression(expression.argument);
|
|
61468
|
-
}
|
|
61469
|
-
if (!isNodeOfType(expression, "Identifier")) return;
|
|
61470
|
-
const symbol = scopes.symbolFor(expression);
|
|
61471
|
-
if (!symbol || symbol.references.some((reference) => reference.flag !== "read")) return;
|
|
61472
|
-
const existingConstraint = constraintsBySymbolId.get(symbol.id);
|
|
61473
|
-
constraintsBySymbolId.set(symbol.id, existingConstraint === void 0 || existingConstraint === requiredValue ? requiredValue : null);
|
|
61474
|
-
};
|
|
61475
|
-
let current = node;
|
|
61476
|
-
while (current?.parent && current !== functionBoundary) {
|
|
61477
|
-
const parent = current.parent;
|
|
61478
|
-
if (isNodeOfType(parent, "IfStatement")) {
|
|
61479
|
-
if (parent.consequent === current) recordConstraint(parent.test, true);
|
|
61480
|
-
if (parent.alternate === current) recordConstraint(parent.test, false);
|
|
61481
|
-
} else if (isNodeOfType(parent, "ConditionalExpression")) {
|
|
61482
|
-
if (parent.consequent === current) recordConstraint(parent.test, true);
|
|
61483
|
-
if (parent.alternate === current) recordConstraint(parent.test, false);
|
|
61484
|
-
} else if (isNodeOfType(parent, "LogicalExpression") && parent.right === current) {
|
|
61485
|
-
if (parent.operator === "&&") recordConstraint(parent.left, true);
|
|
61486
|
-
if (parent.operator === "||") recordConstraint(parent.left, false);
|
|
61487
|
-
}
|
|
61488
|
-
if (current !== node && isFunctionLike$1(current)) break;
|
|
61489
|
-
current = parent;
|
|
61490
|
-
}
|
|
61491
|
-
return constraintsBySymbolId;
|
|
61492
|
-
};
|
|
61493
|
-
const haveContradictoryStableBooleanGuards = (firstNode, secondNode, functionBoundary, scopes) => {
|
|
61494
|
-
const firstConstraints = collectStableBooleanGuardConstraints(firstNode, functionBoundary, scopes);
|
|
61495
|
-
const secondConstraints = collectStableBooleanGuardConstraints(secondNode, functionBoundary, scopes);
|
|
61496
|
-
for (const requiredValue of firstConstraints.values()) if (requiredValue === null) return true;
|
|
61497
|
-
for (const requiredValue of secondConstraints.values()) if (requiredValue === null) return true;
|
|
61498
|
-
for (const [symbolId, firstRequiredValue] of firstConstraints) {
|
|
61499
|
-
const secondRequiredValue = secondConstraints.get(symbolId);
|
|
61500
|
-
if (firstRequiredValue !== null && secondRequiredValue !== void 0 && secondRequiredValue !== null && firstRequiredValue !== secondRequiredValue) return true;
|
|
61501
|
-
}
|
|
61502
|
-
return false;
|
|
61503
|
-
};
|
|
61504
|
-
const isInsideIntrinsicReactEventHandlerAttribute = (node, functionBoundary) => {
|
|
61505
|
-
let current = node;
|
|
61506
|
-
while (current && current !== functionBoundary) {
|
|
61507
|
-
if (isEventHandlerAttribute(current) && isJsxAttributeOnIntrinsicHtmlElement(current)) return true;
|
|
61508
|
-
current = current.parent;
|
|
61509
|
-
}
|
|
61510
|
-
return false;
|
|
61511
|
-
};
|
|
61512
|
-
const getTransparentExpressionBindingIdentifier = (expression) => {
|
|
61513
|
-
const expressionRoot = findTransparentExpressionRoot(expression);
|
|
61514
|
-
const parent = expressionRoot.parent;
|
|
61515
|
-
if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) return parent.id;
|
|
61516
|
-
if (isNodeOfType(parent, "AssignmentExpression") && parent.right === expressionRoot && isNodeOfType(parent.left, "Identifier")) return parent.left;
|
|
61517
|
-
return null;
|
|
61518
|
-
};
|
|
61519
|
-
const getIntrinsicReactEventHandlerBindingIdentifier = (functionNode, index) => {
|
|
61520
|
-
if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
|
|
61521
|
-
const functionExpressionRoot = findTransparentExpressionRoot(functionNode);
|
|
61522
|
-
const directTransparentBindingIdentifier = getTransparentExpressionBindingIdentifier(functionExpressionRoot);
|
|
61523
|
-
if (directTransparentBindingIdentifier) return directTransparentBindingIdentifier;
|
|
61524
|
-
const callbackCall = functionExpressionRoot.parent;
|
|
61525
|
-
if (!isNodeOfType(callbackCall, "CallExpression") || callbackCall.arguments?.[0] !== functionExpressionRoot || !isReactApiCall(callbackCall, "useCallback", index.context.scopes, { resolveNamedAliases: true })) return null;
|
|
61526
|
-
return getTransparentExpressionBindingIdentifier(callbackCall);
|
|
61527
|
-
};
|
|
61528
|
-
const isExclusiveIntrinsicReactEventHandler = (functionNode, index) => {
|
|
61529
|
-
const bindingIdentifier = getIntrinsicReactEventHandlerBindingIdentifier(functionNode, index);
|
|
61530
|
-
if (!bindingIdentifier) return isInsideIntrinsicReactEventHandlerAttribute(functionNode, index.componentFunction);
|
|
61531
|
-
const bindingSymbol = index.context.scopes.symbolFor(bindingIdentifier);
|
|
61532
|
-
return Boolean(bindingSymbol && bindingSymbol.references.length > 0 && bindingSymbol.references.every((reference) => isInsideIntrinsicReactEventHandlerAttribute(reference.identifier, index.componentFunction)));
|
|
61533
|
-
};
|
|
61534
|
-
const canNodeReachNode = (sourceNode, targetNode, index) => {
|
|
61535
|
-
const { context } = index;
|
|
61536
|
-
if (!isNodeReachableWithinFunction(sourceNode, context)) return false;
|
|
61537
|
-
if (!isNodeReachableWithinFunction(targetNode, context)) return false;
|
|
61538
|
-
const sourceOwner = context.cfg.enclosingFunction(sourceNode);
|
|
61539
|
-
const targetOwner = context.cfg.enclosingFunction(targetNode);
|
|
61540
|
-
if (!sourceOwner || sourceOwner !== targetOwner) return false;
|
|
61541
|
-
if (areInMutuallyExclusiveConditionalBranches(sourceNode, targetNode)) return false;
|
|
61542
|
-
if (haveContradictoryStableBooleanGuards(sourceNode, targetNode, sourceOwner, context.scopes)) return false;
|
|
61543
|
-
if (isDescendantWithoutFunctionBoundary(targetNode, sourceNode)) return false;
|
|
61544
|
-
if (isDescendantWithoutFunctionBoundary(sourceNode, targetNode)) return true;
|
|
61545
|
-
const functionCfg = context.cfg.cfgFor(sourceOwner);
|
|
61546
|
-
const sourceBlock = functionCfg?.blockOf(sourceNode);
|
|
61547
|
-
const targetBlock = functionCfg?.blockOf(targetNode);
|
|
61548
|
-
if (!functionCfg || !sourceBlock || !targetBlock) return false;
|
|
61549
|
-
if (sourceBlock === targetBlock) {
|
|
61550
|
-
const sourceStart = getRangeStart(sourceNode);
|
|
61551
|
-
const targetStart = getRangeStart(targetNode);
|
|
61552
|
-
return sourceStart !== null && targetStart !== null && sourceStart < targetStart;
|
|
61553
|
-
}
|
|
61554
|
-
const visitedBlocks = new Set([sourceBlock]);
|
|
61555
|
-
const pendingBlocks = sourceBlock.successors.filter((edge) => edge.kind !== "throw").map((edge) => edge.to);
|
|
61556
|
-
while (pendingBlocks.length > 0) {
|
|
61557
|
-
const block = pendingBlocks.pop();
|
|
61558
|
-
if (!block || visitedBlocks.has(block)) continue;
|
|
61559
|
-
if (block === targetBlock) return true;
|
|
61560
|
-
visitedBlocks.add(block);
|
|
61561
|
-
for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
|
|
61562
|
-
}
|
|
61563
|
-
return false;
|
|
61564
|
-
};
|
|
61565
|
-
const getInlineIntrinsicHandlerJsxElement = (functionNode) => {
|
|
61566
|
-
let currentNode = functionNode;
|
|
61567
|
-
while (currentNode) {
|
|
61568
|
-
if (isNodeOfType(currentNode, "JSXAttribute") && isEventHandlerAttribute(currentNode) && isJsxAttributeOnIntrinsicHtmlElement(currentNode)) {
|
|
61569
|
-
const openingElement = currentNode.parent;
|
|
61570
|
-
return isNodeOfType(openingElement?.parent, "JSXElement") ? openingElement.parent : null;
|
|
61571
|
-
}
|
|
61572
|
-
if (currentNode !== functionNode && isFunctionLike$1(currentNode)) return null;
|
|
61573
|
-
currentNode = currentNode.parent;
|
|
61574
|
-
}
|
|
61575
|
-
return null;
|
|
61576
|
-
};
|
|
61577
|
-
const isProvenNonEscapingVoidRead = (identifier) => {
|
|
61578
|
-
const expressionRoot = findTransparentExpressionRoot(identifier);
|
|
61579
|
-
return isNodeOfType(expressionRoot.parent, "UnaryExpression") && expressionRoot.parent.operator === "void" && expressionRoot.parent.argument === expressionRoot;
|
|
61580
|
-
};
|
|
61581
|
-
const getDirectReadonlyValueAliasSymbol = (referenceIdentifier, sourceSymbol, index) => {
|
|
61582
|
-
const referenceRoot = findTransparentExpressionRoot(referenceIdentifier);
|
|
61583
|
-
const parent = referenceRoot.parent;
|
|
61584
|
-
if (isNodeOfType(parent, "VariableDeclarator") && parent.init === referenceRoot && isNodeOfType(parent.id, "Identifier")) {
|
|
61585
|
-
const aliasSymbol = index.context.scopes.symbolFor(parent.id);
|
|
61586
|
-
const aliasInitializer = aliasSymbol ? getDirectConstInitializer(aliasSymbol) : null;
|
|
61587
|
-
const unwrappedInitializer = aliasInitializer ? stripParenExpression(aliasInitializer) : null;
|
|
61588
|
-
return aliasSymbol && isNodeOfType(unwrappedInitializer, "Identifier") && index.context.scopes.symbolFor(unwrappedInitializer) === sourceSymbol ? aliasSymbol : null;
|
|
61589
|
-
}
|
|
61590
|
-
if (!isNodeOfType(parent, "CallExpression") || parent.arguments?.[0] !== referenceRoot || !isReactApiCall(parent, "useCallback", index.context.scopes, { resolveNamedAliases: true })) return null;
|
|
61591
|
-
const callbackResultRoot = findTransparentExpressionRoot(parent);
|
|
61592
|
-
const resultParent = callbackResultRoot.parent;
|
|
61593
|
-
if (!isNodeOfType(resultParent, "VariableDeclarator") || resultParent.init !== callbackResultRoot || !isNodeOfType(resultParent.id, "Identifier")) return null;
|
|
61594
|
-
const callbackResultSymbol = index.context.scopes.symbolFor(resultParent.id);
|
|
61595
|
-
return callbackResultSymbol && getDirectConstInitializer(callbackResultSymbol) === callbackResultRoot ? callbackResultSymbol : null;
|
|
61596
|
-
};
|
|
61597
|
-
const isKnownAggregateMutationReference = (referenceIdentifier, index) => {
|
|
61598
|
-
const referenceRoot = findTransparentExpressionRoot(referenceIdentifier);
|
|
61599
|
-
const parent = referenceRoot.parent;
|
|
61600
|
-
if (isNodeOfType(parent, "MemberExpression") && parent.object === referenceRoot) {
|
|
61601
|
-
const memberRoot = findTransparentExpressionRoot(parent);
|
|
61602
|
-
const callExpression = memberRoot.parent;
|
|
61603
|
-
const methodName = getStaticPropertyName(parent);
|
|
61604
|
-
if (isNodeOfType(callExpression, "CallExpression") && callExpression.callee === memberRoot && methodName !== null && AGGREGATE_MUTATION_METHOD_NAMES.has(methodName)) return true;
|
|
61605
|
-
}
|
|
61606
|
-
if (!isNodeOfType(parent, "CallExpression") || parent.arguments?.[0] !== referenceRoot) return false;
|
|
61607
|
-
const callee = stripParenExpression(parent.callee);
|
|
61608
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
61609
|
-
const methodName = getStaticPropertyName(callee);
|
|
61610
|
-
if (methodName === null) return false;
|
|
61611
|
-
return OBJECT_AGGREGATE_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "Object", index.context.scopes) || REFLECT_AGGREGATE_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "Reflect", index.context.scopes);
|
|
61612
|
-
};
|
|
61613
|
-
const collectExactReadonlyValueEscapeAnchors = (symbol, index, valueFunctionNode, options = {}, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
61614
|
-
if (visitedSymbolIds.has(symbol.id)) return null;
|
|
61615
|
-
if (symbol.references.some((reference) => reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier) || options.rejectKnownMutations && isKnownAggregateMutationReference(reference.identifier, index))) return null;
|
|
61616
|
-
const nextVisitedSymbolIds = new Set(visitedSymbolIds);
|
|
61617
|
-
nextVisitedSymbolIds.add(symbol.id);
|
|
61618
|
-
const escapeAnchors = [];
|
|
61619
|
-
for (const reference of symbol.references) {
|
|
61620
|
-
if (valueFunctionNode && index.context.cfg.enclosingFunction(reference.identifier) === valueFunctionNode || isProvenNonEscapingVoidRead(reference.identifier)) continue;
|
|
61621
|
-
const aliasSymbol = getDirectReadonlyValueAliasSymbol(reference.identifier, symbol, index);
|
|
61622
|
-
if (!aliasSymbol) {
|
|
61623
|
-
escapeAnchors.push(reference.identifier);
|
|
61624
|
-
continue;
|
|
61625
|
-
}
|
|
61626
|
-
const aliasEscapeAnchors = collectExactReadonlyValueEscapeAnchors(aliasSymbol, index, valueFunctionNode, options, nextVisitedSymbolIds);
|
|
61627
|
-
if (!aliasEscapeAnchors) return null;
|
|
61628
|
-
escapeAnchors.push(...aliasEscapeAnchors);
|
|
61629
|
-
}
|
|
61630
|
-
return escapeAnchors.length > 0 ? escapeAnchors : null;
|
|
61631
|
-
};
|
|
61632
|
-
const findReadonlyJsxAggregateRoot = (jsxElement) => {
|
|
61633
|
-
let aggregateRoot = findTransparentExpressionRoot(jsxElement);
|
|
61634
|
-
while (aggregateRoot.parent) {
|
|
61635
|
-
const parent = aggregateRoot.parent;
|
|
61636
|
-
if ((isNodeOfType(parent, "JSXElement") || isNodeOfType(parent, "JSXFragment")) && parent.children?.some((child) => child === aggregateRoot)) {
|
|
61637
|
-
aggregateRoot = parent;
|
|
61638
|
-
continue;
|
|
61639
|
-
}
|
|
61640
|
-
if (isNodeOfType(parent, "ArrayExpression") && parent.elements?.some((element) => element === aggregateRoot)) {
|
|
61641
|
-
aggregateRoot = findTransparentExpressionRoot(parent);
|
|
61642
|
-
continue;
|
|
61643
|
-
}
|
|
61644
|
-
if (isNodeOfType(parent, "Property") && parent.value === aggregateRoot && parent.kind === "init" && !parent.computed && isNodeOfType(parent.parent, "ObjectExpression")) {
|
|
61645
|
-
aggregateRoot = findTransparentExpressionRoot(parent.parent);
|
|
61646
|
-
continue;
|
|
61647
|
-
}
|
|
61648
|
-
if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === aggregateRoot || parent.alternate === aggregateRoot)) {
|
|
61649
|
-
aggregateRoot = findTransparentExpressionRoot(parent);
|
|
61650
|
-
continue;
|
|
61651
|
-
}
|
|
61652
|
-
if (isNodeOfType(parent, "LogicalExpression") && (parent.right === aggregateRoot || parent.left === aggregateRoot && parent.operator !== "&&")) {
|
|
61653
|
-
aggregateRoot = findTransparentExpressionRoot(parent);
|
|
61654
|
-
continue;
|
|
61655
|
-
}
|
|
61656
|
-
if (isNodeOfType(parent, "SequenceExpression") && getFinalSequenceExpressionValue(parent) === getFinalSequenceExpressionValue(aggregateRoot)) {
|
|
61657
|
-
aggregateRoot = findTransparentExpressionRoot(parent);
|
|
61658
|
-
continue;
|
|
61659
|
-
}
|
|
61660
|
-
break;
|
|
61661
|
-
}
|
|
61662
|
-
return aggregateRoot;
|
|
61663
|
-
};
|
|
61664
|
-
const getReadonlyJsxValueEscapeAnchors = (jsxElement, index) => {
|
|
61665
|
-
const jsxValueRoot = findReadonlyJsxAggregateRoot(jsxElement);
|
|
61666
|
-
const parent = jsxValueRoot.parent;
|
|
61667
|
-
if (!isNodeOfType(parent, "VariableDeclarator") || parent.init !== jsxValueRoot) return [jsxValueRoot];
|
|
61668
|
-
if (!isNodeOfType(parent.id, "Identifier")) return null;
|
|
61669
|
-
const bindingSymbol = index.context.scopes.symbolFor(parent.id);
|
|
61670
|
-
if (!bindingSymbol || getDirectConstInitializer(bindingSymbol) !== jsxValueRoot) return null;
|
|
61671
|
-
return collectExactReadonlyValueEscapeAnchors(bindingSymbol, index, null, { rejectKnownMutations: true });
|
|
61672
|
-
};
|
|
61673
|
-
const getExclusiveIntrinsicReactEventHandlerAnchors = (functionNode, index) => {
|
|
61674
|
-
if (!isExclusiveIntrinsicReactEventHandler(functionNode, index)) return null;
|
|
61675
|
-
const bindingIdentifier = getIntrinsicReactEventHandlerBindingIdentifier(functionNode, index);
|
|
61676
|
-
if (!bindingIdentifier) {
|
|
61677
|
-
const jsxElement = getInlineIntrinsicHandlerJsxElement(functionNode);
|
|
61678
|
-
return jsxElement ? getReadonlyJsxValueEscapeAnchors(jsxElement, index) : null;
|
|
61679
|
-
}
|
|
61680
|
-
return index.context.scopes.symbolFor(bindingIdentifier)?.references.map((reference) => reference.identifier) ?? null;
|
|
61681
|
-
};
|
|
61682
|
-
const getReadonlyFunctionEscapeAnchors = (functionNode, index) => {
|
|
61683
|
-
const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
|
|
61684
|
-
if (!bindingIdentifier) return null;
|
|
61685
|
-
const bindingSymbol = index.context.scopes.symbolFor(bindingIdentifier);
|
|
61686
|
-
if (!bindingSymbol || resolveExactLocalFunction(bindingIdentifier, index.context.scopes) !== functionNode) return null;
|
|
61687
|
-
return collectExactReadonlyValueEscapeAnchors(bindingSymbol, index, functionNode);
|
|
61688
|
-
};
|
|
61689
|
-
const isAliasInitializedBeforeExecution = (aliasDeclaration, executionNode, index, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
|
|
61690
|
-
if (!isNodeReachableWithinFunction(aliasDeclaration, index.context)) return false;
|
|
61691
|
-
const aliasOwner = index.context.cfg.enclosingFunction(aliasDeclaration);
|
|
61692
|
-
const executionOwner = index.context.cfg.enclosingFunction(executionNode);
|
|
61693
|
-
if (!aliasOwner || !executionOwner) return false;
|
|
61694
|
-
if (aliasOwner === executionOwner) return canNodeReachNode(aliasDeclaration, executionNode, index);
|
|
61695
|
-
if (visitedFunctionNodes.has(executionOwner)) return false;
|
|
61696
|
-
const nextVisitedFunctionNodes = new Set(visitedFunctionNodes);
|
|
61697
|
-
nextVisitedFunctionNodes.add(executionOwner);
|
|
61698
|
-
if (aliasOwner === index.componentFunction && (index.effectCallbacks.has(executionOwner) || index.mountedListenerFunctions.has(executionOwner))) return doNodesCoverEveryPathFromFunctionEntry(index.componentFunction, [aliasDeclaration], index.context, { ignoreThrowEdges: true });
|
|
61699
|
-
const invocations = new Set([...index.callSitesByFunction.get(executionOwner) ?? [], ...index.synchronousInvocationsByFunction.get(executionOwner) ?? []]);
|
|
61700
|
-
if (invocations.size > 0) return [...invocations].every((invocation) => isAliasInitializedBeforeExecution(aliasDeclaration, invocation, index, nextVisitedFunctionNodes));
|
|
61701
|
-
const deferredExecutionAnchors = getExclusiveIntrinsicReactEventHandlerAnchors(executionOwner, index);
|
|
61702
|
-
if (deferredExecutionAnchors) return deferredExecutionAnchors.every((anchor) => isAliasInitializedBeforeExecution(aliasDeclaration, anchor, index, nextVisitedFunctionNodes));
|
|
61703
|
-
const functionEscapeAnchors = getReadonlyFunctionEscapeAnchors(executionOwner, index);
|
|
61704
|
-
return Boolean(functionEscapeAnchors?.every((anchor) => isAliasInitializedBeforeExecution(aliasDeclaration, anchor, index, nextVisitedFunctionNodes)));
|
|
61705
|
-
};
|
|
61706
|
-
const resolveExactReadonlyCalleeSymbolId = (callExpression, index) => {
|
|
61707
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return null;
|
|
61708
|
-
const callee = stripParenExpression(callExpression.callee);
|
|
61709
|
-
if (!isNodeOfType(callee, "Identifier")) return null;
|
|
61710
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
61711
|
-
let symbol = index.context.scopes.symbolFor(callee);
|
|
61712
|
-
while (symbol) {
|
|
61713
|
-
if (visitedSymbolIds.has(symbol.id)) return null;
|
|
61714
|
-
visitedSymbolIds.add(symbol.id);
|
|
61715
|
-
const initializer = getDirectConstInitializer(symbol);
|
|
61716
|
-
if (!initializer) return symbol.id;
|
|
61717
|
-
if (symbol.references.some((reference) => reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier)) || !isAliasInitializedBeforeExecution(symbol.declarationNode, callExpression, index)) return null;
|
|
61718
|
-
const unwrappedInitializer = stripParenExpression(initializer);
|
|
61719
|
-
if (!isNodeOfType(unwrappedInitializer, "Identifier")) return symbol.id;
|
|
61720
|
-
const initializerSymbol = index.context.scopes.symbolFor(unwrappedInitializer);
|
|
61721
|
-
if (!initializerSymbol || !isAliasInitializedBeforeExecution(initializerSymbol.declarationNode, symbol.declarationNode, index)) return null;
|
|
61722
|
-
symbol = initializerSymbol;
|
|
61723
|
-
}
|
|
61724
|
-
return null;
|
|
61725
|
-
};
|
|
61726
|
-
const collectExactReadonlyCalleeCalls = (index) => {
|
|
61727
|
-
for (const callExpression of index.identifierCalls) {
|
|
61728
|
-
const calleeSymbolId = resolveExactReadonlyCalleeSymbolId(callExpression, index);
|
|
61729
|
-
if (calleeSymbolId !== null) addToSetIndex(index.callsByCalleeSymbolId, calleeSymbolId, callExpression);
|
|
61730
|
-
}
|
|
61731
|
-
};
|
|
61732
|
-
const canNodeReachNormalFunctionExit = (node, functionNode, index) => {
|
|
61733
|
-
const functionCfg = index.context.cfg.cfgFor(functionNode);
|
|
61734
|
-
const sourceBlock = functionCfg?.blockOf(node);
|
|
61735
|
-
if (!functionCfg || !sourceBlock) return false;
|
|
61736
|
-
const visitedBlocks = /* @__PURE__ */ new Set();
|
|
61737
|
-
const pendingBlocks = [sourceBlock];
|
|
61738
|
-
while (pendingBlocks.length > 0) {
|
|
61739
|
-
const block = pendingBlocks.pop();
|
|
61740
|
-
if (!block || visitedBlocks.has(block)) continue;
|
|
61741
|
-
visitedBlocks.add(block);
|
|
61742
|
-
for (const edge of block.successors) {
|
|
61743
|
-
if (edge.kind === "throw") continue;
|
|
61744
|
-
if (edge.to === functionCfg.exit) return true;
|
|
61745
|
-
pendingBlocks.push(edge.to);
|
|
61746
|
-
}
|
|
61747
|
-
}
|
|
61748
|
-
return false;
|
|
61749
|
-
};
|
|
61750
|
-
const canExecuteBeforeAsyncSuspension = (node, functionNode, index) => {
|
|
61751
|
-
if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, index.context);
|
|
61752
|
-
const functionCfg = index.context.cfg.cfgFor(functionNode);
|
|
61753
|
-
const targetBlock = functionCfg?.blockOf(node);
|
|
61754
|
-
if (!functionCfg || !targetBlock) return false;
|
|
61755
|
-
const awaitsByBlock = /* @__PURE__ */ new Map();
|
|
61756
|
-
for (const awaitExpression of index.awaitExpressionsByOwner.get(functionNode) ?? []) {
|
|
61757
|
-
const awaitBlock = functionCfg.blockOf(awaitExpression);
|
|
61758
|
-
if (!awaitBlock) continue;
|
|
61759
|
-
const blockAwaits = awaitsByBlock.get(awaitBlock) ?? [];
|
|
61760
|
-
blockAwaits.push(awaitExpression);
|
|
61761
|
-
awaitsByBlock.set(awaitBlock, blockAwaits);
|
|
61762
|
-
}
|
|
61763
|
-
const visitedBlocks = /* @__PURE__ */ new Set();
|
|
61764
|
-
const pendingBlocks = [functionCfg.entry];
|
|
61765
|
-
const targetStart = getRangeStart(node);
|
|
61766
|
-
while (pendingBlocks.length > 0) {
|
|
61767
|
-
const block = pendingBlocks.pop();
|
|
61768
|
-
if (!block || visitedBlocks.has(block)) continue;
|
|
61769
|
-
visitedBlocks.add(block);
|
|
61770
|
-
const blockAwaits = awaitsByBlock.get(block) ?? [];
|
|
61771
|
-
if (block === targetBlock) return !blockAwaits.some((awaitExpression) => {
|
|
61772
|
-
if (isNodeOfType(awaitExpression, "AwaitExpression") && isDescendantWithoutFunctionBoundary(node, awaitExpression.argument)) return false;
|
|
61773
|
-
if (isDescendantWithoutFunctionBoundary(awaitExpression, node)) return true;
|
|
61774
|
-
const awaitStart = getRangeStart(awaitExpression);
|
|
61775
|
-
return awaitStart !== null && targetStart !== null && awaitStart < targetStart;
|
|
61776
|
-
});
|
|
61777
|
-
if (blockAwaits.length > 0) continue;
|
|
61778
|
-
for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
|
|
61779
|
-
}
|
|
61780
|
-
return false;
|
|
61781
|
-
};
|
|
61782
|
-
const canReactBatchMutationAfterExecution = (executionNode, mutationNode, owner, index) => (isExclusiveIntrinsicReactEventHandler(owner, index) || index.effectCallbacks.has(owner)) && canNodeReachNode(executionNode, mutationNode, index) && canExecuteBeforeAsyncSuspension(mutationNode, owner, index) && canNodeReachNormalFunctionExit(mutationNode, owner, index);
|
|
61783
|
-
const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFunctions, cycleAffectedFunctions) => {
|
|
61784
|
-
const cachedResult = index.synchronousMutationResultByFunction.get(functionNode);
|
|
61785
|
-
if (cachedResult !== void 0) return cachedResult;
|
|
61786
|
-
if (!isFunctionLike$1(functionNode) || functionNode.generator) return false;
|
|
61787
|
-
if (visitingFunctions.has(functionNode)) {
|
|
61788
|
-
let didReachCycleEntry = false;
|
|
61789
|
-
for (const visitingFunction of visitingFunctions) {
|
|
61790
|
-
if (visitingFunction === functionNode) didReachCycleEntry = true;
|
|
61791
|
-
if (didReachCycleEntry) cycleAffectedFunctions.add(visitingFunction);
|
|
61792
|
-
}
|
|
61793
|
-
return false;
|
|
61794
|
-
}
|
|
61795
|
-
visitingFunctions.add(functionNode);
|
|
61796
|
-
const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
|
|
61797
|
-
visitingFunctions.delete(functionNode);
|
|
61798
|
-
if (doesMutateSynchronously || !cycleAffectedFunctions.has(functionNode)) index.synchronousMutationResultByFunction.set(functionNode, doesMutateSynchronously);
|
|
61799
|
-
return doesMutateSynchronously;
|
|
61800
|
-
};
|
|
61801
|
-
const collectLocationMutationExecutions = (functionNode, index, visitingFunctions = /* @__PURE__ */ new Set(), cycleAffectedFunctions = /* @__PURE__ */ new Set()) => {
|
|
61802
|
-
const cachedExecutions = index.mutationExecutionsByOwner.get(functionNode);
|
|
61803
|
-
if (cachedExecutions) return cachedExecutions;
|
|
61804
|
-
const mutationExecutions = new Set(index.historyMutationsByOwner.get(functionNode) ?? []);
|
|
61805
|
-
for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
|
|
61806
|
-
const calledFunction = index.calledFunctionByExpression.get(expression);
|
|
61807
|
-
if (calledFunction && functionMaySynchronouslyMutateLocation(calledFunction, index, visitingFunctions, cycleAffectedFunctions)) mutationExecutions.add(expression);
|
|
61808
|
-
for (const callbackFunction of index.synchronousCallbacksByExpression.get(expression) ?? []) if (functionMaySynchronouslyMutateLocation(callbackFunction, index, visitingFunctions, cycleAffectedFunctions)) mutationExecutions.add(expression);
|
|
61809
|
-
}
|
|
61810
|
-
if (!cycleAffectedFunctions.has(functionNode)) index.mutationExecutionsByOwner.set(functionNode, mutationExecutions);
|
|
61811
|
-
return mutationExecutions;
|
|
61812
|
-
};
|
|
61813
|
-
const isDefinitelyMatchingLocationListenerRemoval = (registration, removal) => registration.eventName === removal.eventName && registration.listenerFunction === removal.listenerFunction && registration.capture !== null && removal.capture !== null && registration.capture === removal.capture;
|
|
61814
|
-
const functionMustSynchronouslyRemoveLocationListener = (functionNode, registration, index, visitingFunctions) => {
|
|
61815
|
-
if (!isFunctionLike$1(functionNode) || functionNode.generator) return false;
|
|
61816
|
-
if (visitingFunctions.has(functionNode)) return false;
|
|
61817
|
-
const nextVisitingFunctions = new Set(visitingFunctions);
|
|
61818
|
-
nextVisitingFunctions.add(functionNode);
|
|
61819
|
-
const removalExecutions = [];
|
|
61820
|
-
for (const removal of index.listenerRemovals) {
|
|
61821
|
-
if (!isDefinitelyMatchingLocationListenerRemoval(registration, removal)) continue;
|
|
61822
|
-
if (index.context.cfg.enclosingFunction(removal.callExpression) !== functionNode) continue;
|
|
61823
|
-
if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.push(removal.callExpression);
|
|
61824
|
-
}
|
|
61825
|
-
for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
|
|
61826
|
-
const calledFunction = index.calledFunctionByExpression.get(expression);
|
|
61827
|
-
if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
|
|
61828
|
-
}
|
|
61829
|
-
return doNodesCoverEveryPathFromFunctionEntry(functionNode, removalExecutions, index.context);
|
|
61830
|
-
};
|
|
61831
|
-
const collectSynchronousLocationListenerRemovalExecutions = (functionNode, registration, index) => {
|
|
61832
|
-
const removalExecutions = /* @__PURE__ */ new Set();
|
|
61833
|
-
for (const removal of index.listenerRemovals) if (isDefinitelyMatchingLocationListenerRemoval(registration, removal) && index.context.cfg.enclosingFunction(removal.callExpression) === functionNode && canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.add(removal.callExpression);
|
|
61834
|
-
for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
|
|
61835
|
-
const calledFunction = index.calledFunctionByExpression.get(expression);
|
|
61836
|
-
if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
|
|
61837
|
-
}
|
|
61838
|
-
return removalExecutions;
|
|
61839
|
-
};
|
|
61840
|
-
const collectImpliedExpressionExecutionBoundaries = (node, owner) => {
|
|
61841
|
-
const expressionBoundaries = [];
|
|
61842
|
-
let currentChild = node;
|
|
61843
|
-
let currentParent = currentChild.parent ?? null;
|
|
61844
|
-
while (currentParent && currentParent !== owner) {
|
|
61845
|
-
if (isNodeOfType(currentParent, "ConditionalExpression") && currentParent.test === currentChild) {
|
|
61846
|
-
const staticTestValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.test));
|
|
61847
|
-
if (staticTestValue !== null) expressionBoundaries.push(staticTestValue ? currentParent.consequent : currentParent.alternate);
|
|
61848
|
-
}
|
|
61849
|
-
if (isNodeOfType(currentParent, "LogicalExpression") && currentParent.left === currentChild) {
|
|
61850
|
-
const staticLeftValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.left));
|
|
61851
|
-
if (currentParent.operator === "&&" && staticLeftValue === true || currentParent.operator === "||" && staticLeftValue === false) expressionBoundaries.push(currentParent.right);
|
|
61852
|
-
}
|
|
61853
|
-
if (isNodeOfType(currentParent, "ConditionalExpression") && (currentParent.consequent === currentChild || currentParent.alternate === currentChild) || isNodeOfType(currentParent, "LogicalExpression") && currentParent.right === currentChild || isNodeOfType(currentParent, "AssignmentPattern") && currentParent.right === currentChild) expressionBoundaries.push(currentChild);
|
|
61854
|
-
currentChild = currentParent;
|
|
61855
|
-
currentParent = currentChild.parent ?? null;
|
|
61856
|
-
}
|
|
61857
|
-
return expressionBoundaries;
|
|
61858
|
-
};
|
|
61859
|
-
const canExecutionReachFunctionExitWithoutListenerRemoval = (executionNode, registration, index) => {
|
|
61860
|
-
if (!isNodeReachableWithinFunction(executionNode, index.context)) return false;
|
|
61861
|
-
const owner = index.context.cfg.enclosingFunction(executionNode);
|
|
61862
|
-
if (!owner) return false;
|
|
61863
|
-
const functionCfg = index.context.cfg.cfgFor(owner);
|
|
61864
|
-
const sourceBlock = functionCfg?.blockOf(executionNode);
|
|
61865
|
-
if (!functionCfg || !sourceBlock) return false;
|
|
61866
|
-
const expressionBoundaries = collectImpliedExpressionExecutionBoundaries(executionNode, owner);
|
|
61867
|
-
const matchingRemovalsByBlock = /* @__PURE__ */ new Map();
|
|
61868
|
-
for (const removalExecution of collectSynchronousLocationListenerRemovalExecutions(owner, registration, index)) {
|
|
61869
|
-
const removalBlock = functionCfg.blockOf(removalExecution);
|
|
61870
|
-
if (!removalBlock) continue;
|
|
61871
|
-
const blockRemovals = matchingRemovalsByBlock.get(removalBlock) ?? [];
|
|
61872
|
-
blockRemovals.push(removalExecution);
|
|
61873
|
-
matchingRemovalsByBlock.set(removalBlock, blockRemovals);
|
|
61874
|
-
}
|
|
61875
|
-
const sourceStart = getRangeStart(executionNode);
|
|
61876
|
-
const hasRemovalAfterExecution = (block) => {
|
|
61877
|
-
const removals = (matchingRemovalsByBlock.get(block) ?? []).filter((removal) => {
|
|
61878
|
-
if (block !== sourceBlock) return true;
|
|
61879
|
-
const removalStart = getRangeStart(removal);
|
|
61880
|
-
return sourceStart !== null && removalStart !== null && sourceStart < removalStart;
|
|
61881
|
-
});
|
|
61882
|
-
if (collectExpressionPathCoverageNodes(owner, removals, index.context).size > 0) return true;
|
|
61883
|
-
if (block !== sourceBlock) return false;
|
|
61884
|
-
return expressionBoundaries.some((expressionBoundary) => collectExpressionPathCoverageNodes(owner, removals, index.context, expressionBoundary).size > 0);
|
|
61885
|
-
};
|
|
61886
|
-
const visitedBlocks = /* @__PURE__ */ new Set();
|
|
61887
|
-
const pendingBlocks = [sourceBlock];
|
|
61888
|
-
while (pendingBlocks.length > 0) {
|
|
61889
|
-
const block = pendingBlocks.pop();
|
|
61890
|
-
if (!block || visitedBlocks.has(block)) continue;
|
|
61891
|
-
visitedBlocks.add(block);
|
|
61892
|
-
if (hasRemovalAfterExecution(block)) continue;
|
|
61893
|
-
for (const edge of block.successors) {
|
|
61894
|
-
if (edge.kind === "throw") continue;
|
|
61895
|
-
if (edge.to === functionCfg.exit) return true;
|
|
61896
|
-
pendingBlocks.push(edge.to);
|
|
61897
|
-
}
|
|
61898
|
-
}
|
|
61899
|
-
return false;
|
|
61900
|
-
};
|
|
61901
|
-
const isListenerActiveAtMountedExit = (executionNode, registration, index, visitedExecutions = /* @__PURE__ */ new Set()) => {
|
|
61902
|
-
if (visitedExecutions.has(executionNode)) return false;
|
|
61903
|
-
if (!canExecutionReachFunctionExitWithoutListenerRemoval(executionNode, registration, index)) return false;
|
|
61904
|
-
const owner = index.context.cfg.enclosingFunction(executionNode);
|
|
61905
|
-
if (!owner) return false;
|
|
61906
|
-
if (owner === index.componentFunction || index.effectCallbacks.has(owner)) return true;
|
|
61907
|
-
const nextVisitedExecutions = new Set(visitedExecutions);
|
|
61908
|
-
nextVisitedExecutions.add(executionNode);
|
|
61909
|
-
for (const callSite of index.callSitesByFunction.get(owner) ?? []) if (isListenerActiveAtMountedExit(callSite, registration, index, nextVisitedExecutions)) return true;
|
|
61910
|
-
for (const invocation of index.synchronousInvocationsByFunction.get(owner) ?? []) if (isListenerActiveAtMountedExit(invocation, registration, index, nextVisitedExecutions)) return true;
|
|
61911
|
-
return false;
|
|
61912
|
-
};
|
|
61913
|
-
const collectMountedListenerFunctions = (index) => {
|
|
61914
|
-
for (const registration of index.listenerRegistrations) if (isListenerActiveAtMountedExit(registration.callExpression, registration, index)) index.mountedListenerFunctions.add(registration.listenerFunction);
|
|
61915
|
-
};
|
|
61916
|
-
const setterArgumentMutatesLocation = (setterCall, index) => {
|
|
61917
|
-
if (!isNodeOfType(setterCall, "CallExpression")) return false;
|
|
61918
|
-
return (setterCall.arguments ?? []).some((argument) => {
|
|
61919
|
-
if (isNodeOfType(argument, "SpreadElement")) return false;
|
|
61920
|
-
const updaterFunction = resolveExactLocalFunction(argument, index.context.scopes);
|
|
61921
|
-
return Boolean(isFunctionLike$1(updaterFunction) && functionMaySynchronouslyMutateLocation(updaterFunction, index, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()));
|
|
61922
|
-
});
|
|
61923
|
-
};
|
|
61924
|
-
const executionAnchorInvalidatesLocationSnapshot = (executionAnchor, index, visitedExecutions = /* @__PURE__ */ new Set()) => {
|
|
61925
|
-
if (visitedExecutions.has(executionAnchor)) return false;
|
|
61926
|
-
if (!isNodeReachableWithinFunction(executionAnchor, index.context)) return false;
|
|
61927
|
-
const owner = index.context.cfg.enclosingFunction(executionAnchor);
|
|
61928
|
-
if (!owner) return false;
|
|
61929
|
-
if (index.mountedListenerFunctions.has(owner)) return true;
|
|
61930
|
-
if (setterArgumentMutatesLocation(executionAnchor, index) || [...collectLocationMutationExecutions(owner, index)].some((mutationExecution) => canNodeReachNode(mutationExecution, executionAnchor, index) || canReactBatchMutationAfterExecution(executionAnchor, mutationExecution, owner, index))) return true;
|
|
61931
|
-
const nextVisitedExecutions = new Set(visitedExecutions);
|
|
61932
|
-
nextVisitedExecutions.add(executionAnchor);
|
|
61933
|
-
for (const callSite of index.callSitesByFunction.get(owner) ?? []) if (executionAnchorInvalidatesLocationSnapshot(callSite, index, nextVisitedExecutions)) return true;
|
|
61934
|
-
for (const invocation of index.synchronousInvocationsByFunction.get(owner) ?? []) if (executionAnchorInvalidatesLocationSnapshot(invocation, index, nextVisitedExecutions)) return true;
|
|
61935
|
-
return false;
|
|
61936
|
-
};
|
|
61937
|
-
const setterInvalidatesGlobalLocationSnapshot = (setterBindingIdentifier, index) => {
|
|
61938
|
-
const setterSymbol = index.context.scopes.symbolFor(setterBindingIdentifier);
|
|
61939
|
-
if (!setterSymbol) return false;
|
|
61940
|
-
return [...index.callsByCalleeSymbolId.get(setterSymbol.id) ?? []].some((setterCall) => executionAnchorInvalidatesLocationSnapshot(setterCall, index));
|
|
61941
|
-
};
|
|
61942
|
-
const createExternalLocationInvalidationChecker = ({ componentBody, componentFunction, context, directRenderNames, renderReachableExpressions }) => {
|
|
61943
|
-
if (!hasRenderReachableLocationSnapshotRead(componentBody, renderReachableExpressions, directRenderNames, context.scopes)) return () => false;
|
|
61944
|
-
const locationInvalidationIndex = buildLocationInvalidationIndex(componentBody, componentFunction, context);
|
|
61945
|
-
collectMountedListenerFunctions(locationInvalidationIndex);
|
|
61946
|
-
collectExactReadonlyCalleeCalls(locationInvalidationIndex);
|
|
61947
|
-
return (setterBindingIdentifier) => setterInvalidatesGlobalLocationSnapshot(setterBindingIdentifier, locationInvalidationIndex);
|
|
61948
|
-
};
|
|
61949
|
-
//#endregion
|
|
61950
60171
|
//#region src/plugin/rules/state-and-effects/rerender-state-only-in-handlers.ts
|
|
61951
60172
|
const isInsideConditionTest = (identifier, stopAt) => {
|
|
61952
60173
|
let current = identifier;
|
|
@@ -62068,8 +60289,7 @@ const rerenderStateOnlyInHandlers = defineRule({
|
|
|
62068
60289
|
if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
|
|
62069
60290
|
const bindings = collectUseStateBindings(componentBody, context.scopes);
|
|
62070
60291
|
if (bindings.length === 0) return;
|
|
62071
|
-
|
|
62072
|
-
if (renderReachableExpressions.length === 0) return;
|
|
60292
|
+
if (collectRenderReachableExpressions(componentBody).length === 0) return;
|
|
62073
60293
|
const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody, context.scopes);
|
|
62074
60294
|
const dependencyGraph = buildLocalDependencyGraph(componentBody, eventHandlerReferenceNames);
|
|
62075
60295
|
const directRenderNames = collectRenderReachableNames(componentBody, context.scopes, eventHandlerReferenceNames);
|
|
@@ -62083,22 +60303,12 @@ const rerenderStateOnlyInHandlers = defineRule({
|
|
|
62083
60303
|
for (const effectInfo of effectInfos) for (const dependencyName of effectInfo.dependencyNames) if (!selfEchoValueNames.has(dependencyName)) effectConsumedNames.add(dependencyName);
|
|
62084
60304
|
for (const hookArgumentName of collectCustomHookArgumentNames(componentBody, context.scopes)) effectConsumedNames.add(hookArgumentName);
|
|
62085
60305
|
for (const reachableName of expandTransitiveDependencies(effectConsumedNames, dependencyGraph)) renderReachableNames.add(reachableName);
|
|
62086
|
-
const componentFunction = context.cfg.enclosingFunction(componentBody);
|
|
62087
|
-
const doesSetterInvalidateExternalLocation = isFunctionLike$1(componentFunction) ? createExternalLocationInvalidationChecker({
|
|
62088
|
-
componentBody,
|
|
62089
|
-
componentFunction,
|
|
62090
|
-
context,
|
|
62091
|
-
directRenderNames,
|
|
62092
|
-
renderReachableExpressions
|
|
62093
|
-
}) : () => false;
|
|
62094
60306
|
const calledSetterNames = /* @__PURE__ */ new Set();
|
|
62095
60307
|
walkAst(componentBody, (child) => {
|
|
62096
60308
|
if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && setterNames.has(child.callee.name)) calledSetterNames.add(child.callee.name);
|
|
62097
60309
|
});
|
|
62098
60310
|
for (const binding of bindings) {
|
|
62099
60311
|
if (renderReachableNames.has(binding.valueName)) continue;
|
|
62100
|
-
const setterBindingIdentifier = isNodeOfType(binding.declarator.id, "ArrayPattern") ? binding.declarator.id.elements?.[1] : null;
|
|
62101
|
-
if (setterBindingIdentifier && doesSetterInvalidateExternalLocation(setterBindingIdentifier)) continue;
|
|
62102
60312
|
if (binding.valueName === "_" || binding.valueName.startsWith("_")) continue;
|
|
62103
60313
|
const setterSuffix = binding.setterName.slice(3);
|
|
62104
60314
|
if (/^(TriggerRender|ForceUpdate|Rerender|ForceRender|Tick|Bump|BumpVersion|InvalidateRender|Refresh|Repaint)$/i.test(setterSuffix)) continue;
|
|
@@ -65005,66 +63215,6 @@ const roleHasRequiredAriaProps = defineRule({
|
|
|
65005
63215
|
} })
|
|
65006
63216
|
});
|
|
65007
63217
|
//#endregion
|
|
65008
|
-
//#region src/plugin/constants/global-aria-properties.ts
|
|
65009
|
-
const GLOBAL_ARIA_PROPERTIES = new Set([
|
|
65010
|
-
"aria-atomic",
|
|
65011
|
-
"aria-braillelabel",
|
|
65012
|
-
"aria-brailleroledescription",
|
|
65013
|
-
"aria-busy",
|
|
65014
|
-
"aria-controls",
|
|
65015
|
-
"aria-current",
|
|
65016
|
-
"aria-describedby",
|
|
65017
|
-
"aria-description",
|
|
65018
|
-
"aria-details",
|
|
65019
|
-
"aria-disabled",
|
|
65020
|
-
"aria-dropeffect",
|
|
65021
|
-
"aria-errormessage",
|
|
65022
|
-
"aria-flowto",
|
|
65023
|
-
"aria-grabbed",
|
|
65024
|
-
"aria-haspopup",
|
|
65025
|
-
"aria-hidden",
|
|
65026
|
-
"aria-invalid",
|
|
65027
|
-
"aria-keyshortcuts",
|
|
65028
|
-
"aria-label",
|
|
65029
|
-
"aria-labelledby",
|
|
65030
|
-
"aria-live",
|
|
65031
|
-
"aria-owns",
|
|
65032
|
-
"aria-relevant",
|
|
65033
|
-
"aria-roledescription"
|
|
65034
|
-
]);
|
|
65035
|
-
//#endregion
|
|
65036
|
-
//#region src/plugin/constants/prohibited-aria-properties-by-role.ts
|
|
65037
|
-
const ACCESSIBLE_NAME_PROPERTIES = new Set([
|
|
65038
|
-
"aria-braillelabel",
|
|
65039
|
-
"aria-label",
|
|
65040
|
-
"aria-labelledby"
|
|
65041
|
-
]);
|
|
65042
|
-
const PROHIBITED_ARIA_PROPERTIES_BY_ROLE = {
|
|
65043
|
-
caption: ACCESSIBLE_NAME_PROPERTIES,
|
|
65044
|
-
code: ACCESSIBLE_NAME_PROPERTIES,
|
|
65045
|
-
definition: ACCESSIBLE_NAME_PROPERTIES,
|
|
65046
|
-
deletion: ACCESSIBLE_NAME_PROPERTIES,
|
|
65047
|
-
emphasis: ACCESSIBLE_NAME_PROPERTIES,
|
|
65048
|
-
generic: new Set([
|
|
65049
|
-
"aria-braillelabel",
|
|
65050
|
-
"aria-brailleroledescription",
|
|
65051
|
-
"aria-label",
|
|
65052
|
-
"aria-labelledby",
|
|
65053
|
-
"aria-roledescription"
|
|
65054
|
-
]),
|
|
65055
|
-
insertion: ACCESSIBLE_NAME_PROPERTIES,
|
|
65056
|
-
mark: ACCESSIBLE_NAME_PROPERTIES,
|
|
65057
|
-
none: ACCESSIBLE_NAME_PROPERTIES,
|
|
65058
|
-
paragraph: ACCESSIBLE_NAME_PROPERTIES,
|
|
65059
|
-
presentation: ACCESSIBLE_NAME_PROPERTIES,
|
|
65060
|
-
strong: ACCESSIBLE_NAME_PROPERTIES,
|
|
65061
|
-
subscript: ACCESSIBLE_NAME_PROPERTIES,
|
|
65062
|
-
superscript: ACCESSIBLE_NAME_PROPERTIES,
|
|
65063
|
-
term: ACCESSIBLE_NAME_PROPERTIES,
|
|
65064
|
-
time: ACCESSIBLE_NAME_PROPERTIES,
|
|
65065
|
-
tooltip: ACCESSIBLE_NAME_PROPERTIES
|
|
65066
|
-
};
|
|
65067
|
-
//#endregion
|
|
65068
63218
|
//#region src/plugin/constants/role-supports-aria-props.ts
|
|
65069
63219
|
const ROLE_SUPPORTS_ARIA_PROPS = {
|
|
65070
63220
|
alert: new Set([
|
|
@@ -68165,12 +66315,6 @@ const ROLE_SUPPORTS_ARIA_PROPS = {
|
|
|
68165
66315
|
])
|
|
68166
66316
|
};
|
|
68167
66317
|
//#endregion
|
|
68168
|
-
//#region src/plugin/utils/is-aria-property-supported-by-role.ts
|
|
68169
|
-
const isAriaPropertySupportedByRole = (role, property) => {
|
|
68170
|
-
if (PROHIBITED_ARIA_PROPERTIES_BY_ROLE[role]?.has(property)) return false;
|
|
68171
|
-
return ROLE_SUPPORTS_ARIA_PROPS[role]?.has(property) === true || GLOBAL_ARIA_PROPERTIES.has(property);
|
|
68172
|
-
};
|
|
68173
|
-
//#endregion
|
|
68174
66318
|
//#region src/plugin/rules/a11y/role-supports-aria-props.ts
|
|
68175
66319
|
const buildMessageDefault = (roles, propName) => {
|
|
68176
66320
|
return `Screen reader users get no help from \`${propName}\` because role ${roles.map((role) => `\`${role}\``).join(" / ")} ignores it, so remove it or change the role.`;
|
|
@@ -68207,10 +66351,16 @@ const roleSupportsAriaProps = defineRule({
|
|
|
68207
66351
|
const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
|
|
68208
66352
|
const roleCandidates = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [getImplicitRole(node, elementType, context.scopes)].filter((role) => role !== null);
|
|
68209
66353
|
if (roleCandidates === null || roleCandidates.length === 0) return;
|
|
68210
|
-
|
|
66354
|
+
const supportedSets = [];
|
|
66355
|
+
for (const role of roleCandidates) {
|
|
66356
|
+
if (!VALID_ARIA_ROLES.has(role)) return;
|
|
66357
|
+
const supported = ROLE_SUPPORTS_ARIA_PROPS[role];
|
|
66358
|
+
if (!supported) return;
|
|
66359
|
+
supportedSets.push(supported);
|
|
66360
|
+
}
|
|
68211
66361
|
const isImplicit = !roleAttribute;
|
|
68212
66362
|
for (const { attribute, propName } of ariaAttributes) {
|
|
68213
|
-
if (
|
|
66363
|
+
if (supportedSets.some((supported) => supported.has(propName))) continue;
|
|
68214
66364
|
context.report({
|
|
68215
66365
|
node: attribute,
|
|
68216
66366
|
message: isImplicit ? buildMessageImplicit(roleCandidates[0], propName, elementType) : buildMessageDefault(roleCandidates, propName)
|
|
@@ -76992,7 +75142,7 @@ const EXTERNAL_RULES = [
|
|
|
76992
75142
|
{
|
|
76993
75143
|
key: "react-hooks-js/set-state-in-effect",
|
|
76994
75144
|
source: "react-compiler",
|
|
76995
|
-
severity: "
|
|
75145
|
+
severity: "error"
|
|
76996
75146
|
},
|
|
76997
75147
|
{
|
|
76998
75148
|
key: "react-hooks-js/globals",
|