oxlint-plugin-react-doctor 0.8.1-dev.fe241a9 → 0.8.1
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 +215 -1298
- 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");
|
|
@@ -13864,19 +13852,14 @@ const isAstDescendant = (inner, outer) => {
|
|
|
13864
13852
|
};
|
|
13865
13853
|
//#endregion
|
|
13866
13854
|
//#region src/plugin/utils/react-ref-origin.ts
|
|
13867
|
-
const
|
|
13868
|
-
const resolveReactRefSymbol = (memberExpression, scopes, options = {}) => {
|
|
13855
|
+
const resolveReactRefSymbol = (memberExpression, scopes) => {
|
|
13869
13856
|
const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
|
|
13870
13857
|
if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticPropertyName(memberExpression) !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
|
|
13871
13858
|
const symbol = resolveConstIdentifierAlias(receiver, scopes);
|
|
13872
13859
|
if (!symbol?.initializer) return null;
|
|
13873
13860
|
const initializer = stripParenExpression(symbol.initializer);
|
|
13874
13861
|
if (!isNodeOfType(initializer, "CallExpression")) return null;
|
|
13875
|
-
return isReactApiCall(initializer,
|
|
13876
|
-
allowGlobalReactNamespace: true,
|
|
13877
|
-
allowUnboundBareCalls: options.allowUnboundBareCalls,
|
|
13878
|
-
resolveNamedAliases: options.resolveNamedAliases
|
|
13879
|
-
}) ? symbol : null;
|
|
13862
|
+
return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
|
|
13880
13863
|
};
|
|
13881
13864
|
const resolveReactRefCurrentOriginSymbol = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
13882
13865
|
const expression = stripParenExpression(node);
|
|
@@ -13978,18 +13961,6 @@ const isNodeReachableWithinFunction = (node, context) => {
|
|
|
13978
13961
|
//#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
|
|
13979
13962
|
const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
|
|
13980
13963
|
const REPLAYABLE_ITERATOR_COLLECTION_CACHE = /* @__PURE__ */ new WeakMap();
|
|
13981
|
-
const REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES = new Set([
|
|
13982
|
-
"pop",
|
|
13983
|
-
"shift",
|
|
13984
|
-
"splice",
|
|
13985
|
-
"fill",
|
|
13986
|
-
"copyWithin"
|
|
13987
|
-
]);
|
|
13988
|
-
const REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES = new Set([
|
|
13989
|
-
"clear",
|
|
13990
|
-
"delete",
|
|
13991
|
-
"set"
|
|
13992
|
-
]);
|
|
13993
13964
|
const REACT_REF_EFFECT_ANALYSIS_CACHE = /* @__PURE__ */ new WeakMap();
|
|
13994
13965
|
const RESOURCE_NOUN_BY_KIND = {
|
|
13995
13966
|
subscribe: "subscription",
|
|
@@ -14030,92 +14001,6 @@ const resolveExpressionKey = (expression, context, visitedSymbolIds = /* @__PURE
|
|
|
14030
14001
|
}
|
|
14031
14002
|
return null;
|
|
14032
14003
|
};
|
|
14033
|
-
const resolveForEachProjection = (expression, context) => {
|
|
14034
|
-
if (!expression) return null;
|
|
14035
|
-
let currentExpression = stripParenExpression(expression);
|
|
14036
|
-
const memberNames = [];
|
|
14037
|
-
while (isNodeOfType(currentExpression, "MemberExpression") && !currentExpression.computed) {
|
|
14038
|
-
if (!isNodeOfType(currentExpression.property, "Identifier")) return null;
|
|
14039
|
-
memberNames.unshift(currentExpression.property.name);
|
|
14040
|
-
currentExpression = stripParenExpression(currentExpression.object);
|
|
14041
|
-
}
|
|
14042
|
-
if (!isNodeOfType(currentExpression, "Identifier")) return null;
|
|
14043
|
-
const symbol = context.scopes.symbolFor(currentExpression);
|
|
14044
|
-
if (!symbol || symbol.kind !== "parameter") return null;
|
|
14045
|
-
let callbackNode = symbol.bindingIdentifier.parent;
|
|
14046
|
-
while (callbackNode && !isFunctionLike$1(callbackNode)) callbackNode = callbackNode.parent;
|
|
14047
|
-
if (!callbackNode || !isFunctionLike$1(callbackNode)) return null;
|
|
14048
|
-
const forEachCall = findEnclosingForEachCall(callbackNode);
|
|
14049
|
-
if (!forEachCall) return null;
|
|
14050
|
-
const forEachCallee = stripParenExpression(forEachCall.callee);
|
|
14051
|
-
if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
|
|
14052
|
-
const collectionKey = resolveExpressionKey(forEachCallee.object, context);
|
|
14053
|
-
if (!collectionKey) return null;
|
|
14054
|
-
const firstParameter = callbackNode.params[0];
|
|
14055
|
-
const bindingProperty = symbol.bindingIdentifier.parent;
|
|
14056
|
-
const propertyName = isNodeOfType(firstParameter, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.parent === firstParameter && bindingProperty.value === symbol.bindingIdentifier ? getStaticPropertyKeyName(bindingProperty) : null;
|
|
14057
|
-
const parameterProjection = firstParameter === symbol.bindingIdentifier ? "value" : propertyName;
|
|
14058
|
-
if (!parameterProjection) return null;
|
|
14059
|
-
return {
|
|
14060
|
-
collectionKey,
|
|
14061
|
-
projectionKey: [parameterProjection, ...memberNames].join(".")
|
|
14062
|
-
};
|
|
14063
|
-
};
|
|
14064
|
-
const resolveForEachProjectionKey = (expression, context) => {
|
|
14065
|
-
const projection = resolveForEachProjection(expression, context);
|
|
14066
|
-
return projection ? `forEach:${projection.collectionKey}:${projection.projectionKey}` : null;
|
|
14067
|
-
};
|
|
14068
|
-
const resolveResourceIdentityKey = (expression, context) => resolveForEachProjectionKey(expression, context) ?? resolveExpressionKey(expression, context);
|
|
14069
|
-
const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
|
|
14070
|
-
const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
|
|
14071
|
-
if (capture !== null) return `capture:${String(capture)}`;
|
|
14072
|
-
if (!optionsNode) return null;
|
|
14073
|
-
const unwrappedOptions = stripParenExpression(optionsNode);
|
|
14074
|
-
if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
|
|
14075
|
-
const optionsKey = allowOpaqueOptionsIdentity ? resolveResourceIdentityKey(unwrappedOptions, context) : null;
|
|
14076
|
-
return optionsKey ? `options:${optionsKey}` : null;
|
|
14077
|
-
}
|
|
14078
|
-
let captureKey = "capture:false";
|
|
14079
|
-
for (const property of unwrappedOptions.properties ?? []) {
|
|
14080
|
-
if (!isNodeOfType(property, "Property")) {
|
|
14081
|
-
captureKey = null;
|
|
14082
|
-
continue;
|
|
14083
|
-
}
|
|
14084
|
-
const propertyName = getStaticPropertyKeyName(property);
|
|
14085
|
-
if (propertyName === null || !property.computed && propertyName === "__proto__") {
|
|
14086
|
-
captureKey = null;
|
|
14087
|
-
continue;
|
|
14088
|
-
}
|
|
14089
|
-
if (propertyName === "capture") {
|
|
14090
|
-
const propertyValueKey = resolveResourceIdentityKey(property.value, context);
|
|
14091
|
-
captureKey = propertyValueKey ? `capture-value:${propertyValueKey}` : null;
|
|
14092
|
-
}
|
|
14093
|
-
}
|
|
14094
|
-
return captureKey;
|
|
14095
|
-
};
|
|
14096
|
-
const resolveEventListenerCaptureProjection = (optionsNode, context) => {
|
|
14097
|
-
if (!optionsNode) return null;
|
|
14098
|
-
const unwrappedOptions = stripParenExpression(optionsNode);
|
|
14099
|
-
if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return resolveForEachProjection(unwrappedOptions, context);
|
|
14100
|
-
let captureProjection = null;
|
|
14101
|
-
for (const property of unwrappedOptions.properties ?? []) {
|
|
14102
|
-
if (!isNodeOfType(property, "Property")) {
|
|
14103
|
-
captureProjection = null;
|
|
14104
|
-
continue;
|
|
14105
|
-
}
|
|
14106
|
-
const propertyName = getStaticPropertyKeyName(property);
|
|
14107
|
-
if (propertyName === null || !property.computed && propertyName === "__proto__") {
|
|
14108
|
-
captureProjection = null;
|
|
14109
|
-
continue;
|
|
14110
|
-
}
|
|
14111
|
-
if (propertyName === "capture") captureProjection = resolveForEachProjection(property.value, context);
|
|
14112
|
-
}
|
|
14113
|
-
return captureProjection;
|
|
14114
|
-
};
|
|
14115
|
-
const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, context, allowOpaqueOptionsIdentity = false) => {
|
|
14116
|
-
const registrationCaptureKey = resolveEventListenerCaptureIdentityKey(registrationOptions, context, allowOpaqueOptionsIdentity);
|
|
14117
|
-
return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
|
|
14118
|
-
};
|
|
14119
14004
|
const findAssignedResourceKey = (resourceNode, context) => {
|
|
14120
14005
|
let currentNode = resourceNode;
|
|
14121
14006
|
let parentNode = currentNode.parent;
|
|
@@ -14127,30 +14012,6 @@ const findAssignedResourceKey = (resourceNode, context) => {
|
|
|
14127
14012
|
if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
|
|
14128
14013
|
return null;
|
|
14129
14014
|
};
|
|
14130
|
-
const resolveStableMediaQueryListenerIdentityKey = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
14131
|
-
if (!expression) return null;
|
|
14132
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
14133
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
14134
|
-
const symbol = context.scopes.symbolFor(unwrappedExpression);
|
|
14135
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier))) return null;
|
|
14136
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
14137
|
-
if (!initializer) return `symbol:${symbol.id}`;
|
|
14138
|
-
const unwrappedInitializer = stripParenExpression(initializer);
|
|
14139
|
-
if (!isNodeOfType(unwrappedInitializer, "Identifier")) return `symbol:${symbol.id}`;
|
|
14140
|
-
const nextVisitedSymbolIds = new Set(visitedSymbolIds);
|
|
14141
|
-
nextVisitedSymbolIds.add(symbol.id);
|
|
14142
|
-
return resolveStableMediaQueryListenerIdentityKey(unwrappedInitializer, context, nextVisitedSymbolIds) ?? `symbol:${symbol.id}`;
|
|
14143
|
-
}
|
|
14144
|
-
if (isFunctionLike$1(unwrappedExpression)) {
|
|
14145
|
-
const rangeStart = getRangeStart(unwrappedExpression);
|
|
14146
|
-
return rangeStart === null ? null : `function:${rangeStart}`;
|
|
14147
|
-
}
|
|
14148
|
-
return null;
|
|
14149
|
-
};
|
|
14150
|
-
const isProvenLegacyMediaQueryListMethodCall = (callNode, methodName, context) => {
|
|
14151
|
-
const callee = stripParenExpression(callNode.callee);
|
|
14152
|
-
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");
|
|
14153
|
-
};
|
|
14154
14015
|
const getCallRegistrationDetails = (callNode, context) => {
|
|
14155
14016
|
const callee = stripParenExpression(callNode.callee);
|
|
14156
14017
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return {
|
|
@@ -14159,17 +14020,11 @@ const getCallRegistrationDetails = (callNode, context) => {
|
|
|
14159
14020
|
eventKey: null,
|
|
14160
14021
|
handlerKey: null
|
|
14161
14022
|
};
|
|
14162
|
-
if (isProvenLegacyMediaQueryListMethodCall(callNode, "addListener", context)) return {
|
|
14163
|
-
receiverKey: resolveStableMediaQueryListenerIdentityKey(callee.object, context),
|
|
14164
|
-
registrationVerbName: callee.property.name,
|
|
14165
|
-
eventKey: null,
|
|
14166
|
-
handlerKey: resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context)
|
|
14167
|
-
};
|
|
14168
14023
|
return {
|
|
14169
|
-
receiverKey:
|
|
14024
|
+
receiverKey: resolveExpressionKey(callee.object, context),
|
|
14170
14025
|
registrationVerbName: callee.property.name,
|
|
14171
|
-
eventKey:
|
|
14172
|
-
handlerKey:
|
|
14026
|
+
eventKey: resolveExpressionKey(callNode.arguments?.[0], context),
|
|
14027
|
+
handlerKey: resolveExpressionKey(callNode.arguments?.[1], context)
|
|
14173
14028
|
};
|
|
14174
14029
|
};
|
|
14175
14030
|
const findSubscribeLikeUsages = (callback, context) => {
|
|
@@ -14594,8 +14449,8 @@ const isSynchronousIteratorCallback = (functionNode) => {
|
|
|
14594
14449
|
return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments?.[0] === functionNode;
|
|
14595
14450
|
};
|
|
14596
14451
|
const findEnclosingForEachCall = (node) => {
|
|
14597
|
-
const callbackNode =
|
|
14598
|
-
if (!callbackNode
|
|
14452
|
+
const callbackNode = findEnclosingFunction$1(node);
|
|
14453
|
+
if (!callbackNode) return null;
|
|
14599
14454
|
const callNode = callbackNode.parent;
|
|
14600
14455
|
if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments?.[0] !== callbackNode) return null;
|
|
14601
14456
|
const callee = stripParenExpression(callNode.callee);
|
|
@@ -15244,7 +15099,9 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
|
|
|
15244
15099
|
const nodeParameterKey = resolveExpressionKey(usageFunction.params?.[0], context);
|
|
15245
15100
|
const releaseReceiverKey = resolveExpressionKey(releaseCallee.object, context);
|
|
15246
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;
|
|
15247
|
-
|
|
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;
|
|
15248
15105
|
const releaseStart = getRangeStart(releaseCall);
|
|
15249
15106
|
const matchingOwnershipAssignments = [];
|
|
15250
15107
|
const usageFunctionBody = usageFunction.body;
|
|
@@ -15256,126 +15113,6 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
|
|
|
15256
15113
|
const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
|
|
15257
15114
|
return doMatchingNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
|
|
15258
15115
|
};
|
|
15259
|
-
const findDirectExhaustiveForEachCleanupFunction = (releaseNode, requiredCollectionKeys, context) => {
|
|
15260
|
-
let currentNode = findTransparentExpressionRoot(releaseNode);
|
|
15261
|
-
const visitedFunctions = /* @__PURE__ */ new Set();
|
|
15262
|
-
const replayedCollectionKeys = /* @__PURE__ */ new Set();
|
|
15263
|
-
while (true) {
|
|
15264
|
-
const ownerFunction = findEnclosingFunction$1(currentNode);
|
|
15265
|
-
if (!ownerFunction || !isFunctionLike$1(ownerFunction) || visitedFunctions.has(ownerFunction)) return null;
|
|
15266
|
-
visitedFunctions.add(ownerFunction);
|
|
15267
|
-
const isDirectConciseBody = ownerFunction.body === currentNode;
|
|
15268
|
-
const statementNode = currentNode.parent;
|
|
15269
|
-
const isDirectBlockStatement = isNodeOfType(ownerFunction.body, "BlockStatement") && isNodeOfType(statementNode, "ExpressionStatement") && statementNode.parent === ownerFunction.body;
|
|
15270
|
-
if (!isDirectConciseBody && !isDirectBlockStatement || !doMatchingNodesCoverEveryPathFromFunctionEntry(ownerFunction, [isDirectBlockStatement ? statementNode : currentNode], context)) return null;
|
|
15271
|
-
const forEachCall = findEnclosingForEachCall(ownerFunction);
|
|
15272
|
-
if (!forEachCall) return replayedCollectionKeys.size === requiredCollectionKeys.size && isReturnedEffectCleanupFunction(ownerFunction, context) ? ownerFunction : null;
|
|
15273
|
-
const forEachCallee = stripParenExpression(forEachCall.callee);
|
|
15274
|
-
if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
|
|
15275
|
-
const collectionKey = resolveExpressionKey(forEachCallee.object, context);
|
|
15276
|
-
if (!collectionKey || !requiredCollectionKeys.has(collectionKey)) return null;
|
|
15277
|
-
replayedCollectionKeys.add(collectionKey);
|
|
15278
|
-
currentNode = findTransparentExpressionRoot(forEachCall);
|
|
15279
|
-
}
|
|
15280
|
-
};
|
|
15281
|
-
const collectReplayOwnerFunctions = (usageNode) => {
|
|
15282
|
-
const ownerFunctions = /* @__PURE__ */ new Set();
|
|
15283
|
-
let currentNode = usageNode;
|
|
15284
|
-
while (true) {
|
|
15285
|
-
const ownerFunction = findEnclosingFunction$1(currentNode);
|
|
15286
|
-
if (!ownerFunction || !isFunctionLike$1(ownerFunction) || ownerFunctions.has(ownerFunction)) break;
|
|
15287
|
-
ownerFunctions.add(ownerFunction);
|
|
15288
|
-
const forEachCall = findEnclosingForEachCall(ownerFunction);
|
|
15289
|
-
if (!forEachCall) break;
|
|
15290
|
-
currentNode = forEachCall;
|
|
15291
|
-
}
|
|
15292
|
-
return ownerFunctions;
|
|
15293
|
-
};
|
|
15294
|
-
const hasCollectionMutationBeforeRelease = (usageNode, releaseNode, collectionKeys, context) => {
|
|
15295
|
-
const usageStart = getRangeStart(usageNode);
|
|
15296
|
-
const releaseStart = getRangeStart(releaseNode);
|
|
15297
|
-
if (usageStart === null || releaseStart === null) return true;
|
|
15298
|
-
const setupOwnerFunctions = collectReplayOwnerFunctions(usageNode);
|
|
15299
|
-
const cleanupOwnerFunctions = collectReplayOwnerFunctions(releaseNode);
|
|
15300
|
-
let programNode = usageNode;
|
|
15301
|
-
while (programNode.parent) programNode = programNode.parent;
|
|
15302
|
-
let didFindMutation = false;
|
|
15303
|
-
walkAst(programNode, (child) => {
|
|
15304
|
-
if (didFindMutation) return false;
|
|
15305
|
-
const childStart = getRangeStart(child);
|
|
15306
|
-
if (childStart === null) return;
|
|
15307
|
-
const ownerFunction = context.cfg.enclosingFunction(child);
|
|
15308
|
-
if (!ownerFunction) return;
|
|
15309
|
-
const isAfterRegistration = setupOwnerFunctions.has(ownerFunction) && childStart > usageStart;
|
|
15310
|
-
const isBeforeRelease = cleanupOwnerFunctions.has(ownerFunction) && childStart < releaseStart;
|
|
15311
|
-
if (!isAfterRegistration && !isBeforeRelease) return;
|
|
15312
|
-
if (isNodeOfType(child, "AssignmentExpression")) {
|
|
15313
|
-
const assignmentKey = resolveExpressionKey(child.left, context);
|
|
15314
|
-
const assignmentTarget = stripParenExpression(child.left);
|
|
15315
|
-
if (assignmentKey && [...collectionKeys].some((collectionKey) => assignmentKey === collectionKey || assignmentKey === `${collectionKey}.length`) || isNodeOfType(assignmentTarget, "MemberExpression") && assignmentTarget.computed && collectionKeys.has(resolveExpressionKey(assignmentTarget.object, context) ?? "")) {
|
|
15316
|
-
didFindMutation = true;
|
|
15317
|
-
return false;
|
|
15318
|
-
}
|
|
15319
|
-
return;
|
|
15320
|
-
}
|
|
15321
|
-
if (isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
|
|
15322
|
-
const deletedMember = stripParenExpression(child.argument);
|
|
15323
|
-
if (!isNodeOfType(deletedMember, "MemberExpression")) return;
|
|
15324
|
-
if (collectionKeys.has(resolveExpressionKey(deletedMember.object, context) ?? "")) {
|
|
15325
|
-
didFindMutation = true;
|
|
15326
|
-
return false;
|
|
15327
|
-
}
|
|
15328
|
-
return;
|
|
15329
|
-
}
|
|
15330
|
-
if (isNodeOfType(child, "UpdateExpression")) {
|
|
15331
|
-
const updatedKey = resolveExpressionKey(child.argument, context);
|
|
15332
|
-
if (updatedKey && [...collectionKeys].some((collectionKey) => updatedKey === `${collectionKey}.length`)) {
|
|
15333
|
-
didFindMutation = true;
|
|
15334
|
-
return false;
|
|
15335
|
-
}
|
|
15336
|
-
return;
|
|
15337
|
-
}
|
|
15338
|
-
if (!isNodeOfType(child, "CallExpression")) return;
|
|
15339
|
-
const callee = stripParenExpression(child.callee);
|
|
15340
|
-
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;
|
|
15341
|
-
didFindMutation = true;
|
|
15342
|
-
return false;
|
|
15343
|
-
});
|
|
15344
|
-
return didFindMutation;
|
|
15345
|
-
};
|
|
15346
|
-
const usesUnaryListenerSignature = (registrationCall, releaseCall) => getCalleeName$1(registrationCall) === "addListener" && registrationCall.arguments?.length === 1 && releaseCall.arguments?.length === 1;
|
|
15347
|
-
const hasSafeForEachProjectionCleanup = (registrationCall, releaseCall, context) => {
|
|
15348
|
-
const registrationCallee = stripParenExpression(registrationCall.callee);
|
|
15349
|
-
const releaseCallee = stripParenExpression(releaseCall.callee);
|
|
15350
|
-
if (!isNodeOfType(registrationCallee, "MemberExpression") || !isNodeOfType(releaseCallee, "MemberExpression")) return true;
|
|
15351
|
-
const registrationVerbName = getCalleeName$1(registrationCall);
|
|
15352
|
-
const releaseVerbName = getCalleeName$1(releaseCall);
|
|
15353
|
-
const releaseHandler = releaseCall.arguments?.[usesUnaryListenerSignature(registrationCall, releaseCall) ? 0 : 1];
|
|
15354
|
-
const releaseFunction = findEnclosingFunction$1(releaseCall);
|
|
15355
|
-
const registrationEventKey = resolveResourceIdentityKey(registrationCall.arguments?.[0], context);
|
|
15356
|
-
const releaseEventKey = resolveResourceIdentityKey(releaseCall.arguments?.[0], context);
|
|
15357
|
-
const doesHandlerlessOffReleaseEveryRegistration = releaseVerbName === "off" && !releaseHandler && (releaseCall.arguments?.length === 0 || registrationEventKey !== null && registrationEventKey === releaseEventKey);
|
|
15358
|
-
if (Boolean(releaseFunction && isFunctionLike$1(releaseFunction) && isReturnedEffectCleanupFunction(releaseFunction, context) && doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseCall], context)) && (releaseVerbName !== null && UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName) || doesHandlerlessOffReleaseEveryRegistration)) return true;
|
|
15359
|
-
const projections = [
|
|
15360
|
-
registrationCallee.object,
|
|
15361
|
-
registrationCall.arguments?.[0],
|
|
15362
|
-
registrationCall.arguments?.[1],
|
|
15363
|
-
releaseCallee.object,
|
|
15364
|
-
releaseCall.arguments?.[0],
|
|
15365
|
-
releaseCall.arguments?.[1]
|
|
15366
|
-
].flatMap((expression) => {
|
|
15367
|
-
const projection = resolveForEachProjection(expression, context);
|
|
15368
|
-
return projection ? [projection] : [];
|
|
15369
|
-
});
|
|
15370
|
-
if (registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener") for (const optionsNode of [registrationCall.arguments?.[2], releaseCall.arguments?.[2]]) {
|
|
15371
|
-
const captureProjection = resolveEventListenerCaptureProjection(optionsNode, context);
|
|
15372
|
-
if (captureProjection) projections.push(captureProjection);
|
|
15373
|
-
}
|
|
15374
|
-
if (projections.length === 0) return true;
|
|
15375
|
-
const collectionKeys = new Set(projections.map((projection) => projection.collectionKey));
|
|
15376
|
-
if (!findDirectExhaustiveForEachCleanupFunction(releaseCall, collectionKeys, context)) return false;
|
|
15377
|
-
return !hasCollectionMutationBeforeRelease(registrationCall, releaseCall, collectionKeys, context);
|
|
15378
|
-
};
|
|
15379
15116
|
const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
15380
15117
|
const callNode = isNodeOfType(node, "ChainExpression") ? node.expression : node;
|
|
15381
15118
|
if (!isNodeOfType(callNode, "CallExpression")) return false;
|
|
@@ -15391,8 +15128,8 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15391
15128
|
const releaseVerbName = getReleaseVerbName(callNode);
|
|
15392
15129
|
if (!releaseVerbName) return false;
|
|
15393
15130
|
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
|
|
15394
|
-
const releaseReceiverKey =
|
|
15395
|
-
const releaseEventKey =
|
|
15131
|
+
const releaseReceiverKey = resolveExpressionKey(callee.object, context);
|
|
15132
|
+
const releaseEventKey = resolveExpressionKey(callNode.arguments?.[0], context);
|
|
15396
15133
|
const pairedReleaseVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
|
|
15397
15134
|
const pushedResourceCollectionKey = findPushedResourceCollectionKey(usage, context);
|
|
15398
15135
|
const releaseReceiverForOfStatement = findForOfStatementForIteratorExpression(callee.object, context);
|
|
@@ -15402,13 +15139,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15402
15139
|
if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
|
|
15403
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;
|
|
15404
15141
|
if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
|
|
15405
|
-
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;
|
|
15406
15142
|
if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
|
|
15407
|
-
if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
|
|
15408
|
-
if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
|
|
15409
|
-
if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
|
|
15410
|
-
}
|
|
15411
|
-
if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
|
|
15412
15143
|
if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
|
|
15413
15144
|
if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
|
|
15414
15145
|
const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
|
|
@@ -15418,9 +15149,6 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15418
15149
|
if (isAssignmentFormForOfIteratorReference(usageEventArgument, context) || isAssignmentFormForOfIteratorReference(releaseEventArgument, context)) return false;
|
|
15419
15150
|
if (usage.eventKey !== null && releaseEventKey !== null && usage.eventKey !== releaseEventKey) {
|
|
15420
15151
|
if (!isNodeOfType(usage.node, "CallExpression")) return false;
|
|
15421
|
-
const registrationEventProjectionKey = resolveForEachProjectionKey(usage.node.arguments?.[0], context);
|
|
15422
|
-
const releaseEventProjectionKey = resolveForEachProjectionKey(callNode.arguments?.[0], context);
|
|
15423
|
-
if ((registrationEventProjectionKey !== null || releaseEventProjectionKey !== null) && registrationEventProjectionKey !== releaseEventProjectionKey) return false;
|
|
15424
15152
|
const usageForOfStatement = findForOfStatementForIteratorExpression(usageEventArgument, context);
|
|
15425
15153
|
const releaseForOfStatement = findForOfStatementForIteratorExpression(releaseEventArgument, context);
|
|
15426
15154
|
if (usageForOfStatement === null !== (releaseForOfStatement === null)) return false;
|
|
@@ -15434,7 +15162,9 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15434
15162
|
const registrationCallee = stripParenExpression(usage.node.callee);
|
|
15435
15163
|
if (!isNodeOfType(registrationCallee, "MemberExpression")) return false;
|
|
15436
15164
|
if (!isStableLoopReceiver(registrationCallee.object, context) || !isStableLoopReceiver(callee.object, context) || registrationHandlerSymbolId === null || registrationHandlerSymbolId !== releaseHandlerSymbolId) return false;
|
|
15437
|
-
|
|
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;
|
|
15438
15168
|
if (!isDirectExhaustiveForOfRelease(callNode, releaseForOfStatement)) return false;
|
|
15439
15169
|
}
|
|
15440
15170
|
}
|
|
@@ -15443,28 +15173,27 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
|
|
|
15443
15173
|
return isNodeOfType(handlerArgument, "Literal") && handlerArgument.value === null;
|
|
15444
15174
|
}
|
|
15445
15175
|
if (releaseVerbName === "removeEventListener" || releaseVerbName === "removeListener" || releaseVerbName === "off") {
|
|
15446
|
-
const
|
|
15447
|
-
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];
|
|
15448
15178
|
if (!releaseHandler) return releaseVerbName === "off";
|
|
15449
|
-
const expectedHandlerKey =
|
|
15450
|
-
const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[
|
|
15451
|
-
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);
|
|
15452
15182
|
}
|
|
15453
15183
|
if (releaseVerbName === "unobserve" && usage.eventKey !== null) return releaseEventKey === usage.eventKey;
|
|
15454
15184
|
return true;
|
|
15455
15185
|
};
|
|
15456
15186
|
const matchesPairedReleaseVerb = (releaseVerbName, pairedVerbNames) => pairedVerbNames.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName);
|
|
15457
15187
|
const isReturnedEffectCleanupFunction = (functionNode, context) => {
|
|
15458
|
-
|
|
15459
|
-
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
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));
|
|
15468
15197
|
};
|
|
15469
15198
|
const isPotentiallyReachableFunction = (functionNode, context) => {
|
|
15470
15199
|
if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode, context)) return true;
|
|
@@ -15607,9 +15336,11 @@ const isRetainedDisposerRefRelease = (releaseNode, usage, context) => {
|
|
|
15607
15336
|
};
|
|
15608
15337
|
const isSelfReleasingListenerRelease = (releaseNode, releaseFunction, usage, context) => {
|
|
15609
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 });
|
|
15610
15340
|
const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
|
|
15611
15341
|
if (!isNodeOfType(releaseCall, "CallExpression")) return false;
|
|
15612
|
-
|
|
15342
|
+
const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
|
|
15343
|
+
if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
|
|
15613
15344
|
const ownerFunction = findEnclosingFunction$1(releaseFunction);
|
|
15614
15345
|
if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
|
|
15615
15346
|
const triggerRegistrations = [];
|
|
@@ -19079,12 +18810,11 @@ const forbidDomProps = defineRule({
|
|
|
19079
18810
|
* carrying their own near-identical inlined implementation.
|
|
19080
18811
|
*/
|
|
19081
18812
|
const flattenCalleeName = (callee) => {
|
|
19082
|
-
|
|
19083
|
-
if (isNodeOfType(
|
|
19084
|
-
|
|
19085
|
-
const objectName = flattenCalleeName(unwrappedCallee.object);
|
|
18813
|
+
if (isNodeOfType(callee, "Identifier")) return callee.name;
|
|
18814
|
+
if (isNodeOfType(callee, "MemberExpression") && !callee.computed) {
|
|
18815
|
+
const objectName = flattenCalleeName(callee.object);
|
|
19086
18816
|
if (!objectName) return null;
|
|
19087
|
-
if (isNodeOfType(
|
|
18817
|
+
if (isNodeOfType(callee.property, "Identifier")) return `${objectName}.${callee.property.name}`;
|
|
19088
18818
|
}
|
|
19089
18819
|
return null;
|
|
19090
18820
|
};
|
|
@@ -22484,49 +22214,25 @@ const jsLengthCheckFirst = defineRule({
|
|
|
22484
22214
|
});
|
|
22485
22215
|
//#endregion
|
|
22486
22216
|
//#region src/plugin/utils/is-proven-global-namespace-reference.ts
|
|
22487
|
-
const GLOBAL_SELF_PROPERTY_NAMES = new Set([
|
|
22488
|
-
"global",
|
|
22489
|
-
"globalThis",
|
|
22490
|
-
"self",
|
|
22491
|
-
"window"
|
|
22492
|
-
]);
|
|
22493
22217
|
const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
22494
|
-
|
|
22495
|
-
|
|
22496
|
-
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
const declaration = symbol.declarationNode;
|
|
22502
|
-
const initializer = declaration.init;
|
|
22503
|
-
if (!initializer) return false;
|
|
22504
|
-
const isDirectIdentifierAlias = isNodeOfType(declaration.id, "Identifier") && declaration.id === symbol.bindingIdentifier && initializer === symbol.initializer;
|
|
22505
|
-
const bindingProperty = symbol.bindingIdentifier.parent;
|
|
22506
|
-
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
22507
|
-
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);
|
|
22508
|
-
if (!isDirectIdentifierAlias && !isDirectGlobalSelfAlias) return false;
|
|
22509
|
-
visitedSymbolIds.add(symbol.id);
|
|
22510
|
-
currentExpression = initializer;
|
|
22511
|
-
}
|
|
22218
|
+
const strippedExpression = stripParenExpression(expression);
|
|
22219
|
+
if (!isNodeOfType(strippedExpression, "Identifier")) return false;
|
|
22220
|
+
if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
|
|
22221
|
+
const symbol = scopes.symbolFor(strippedExpression);
|
|
22222
|
+
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
22223
|
+
visitedSymbolIds.add(symbol.id);
|
|
22224
|
+
return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
|
|
22512
22225
|
};
|
|
22513
22226
|
const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
22514
|
-
|
|
22515
|
-
|
|
22516
|
-
const strippedExpression = stripParenExpression(currentExpression);
|
|
22517
|
-
if (!isNodeOfType(strippedExpression, "Identifier")) return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
|
|
22227
|
+
const strippedExpression = stripParenExpression(expression);
|
|
22228
|
+
if (isNodeOfType(strippedExpression, "Identifier")) {
|
|
22518
22229
|
if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
|
|
22519
22230
|
const symbol = scopes.symbolFor(strippedExpression);
|
|
22520
|
-
if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
22231
|
+
if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
|
|
22521
22232
|
visitedSymbolIds.add(symbol.id);
|
|
22522
|
-
|
|
22523
|
-
const bindingIdentifier = symbol.bindingIdentifier;
|
|
22524
|
-
const bindingProperty = bindingIdentifier.parent;
|
|
22525
|
-
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);
|
|
22526
|
-
const directInitializer = getDirectConstInitializer(symbol);
|
|
22527
|
-
if (!directInitializer) return false;
|
|
22528
|
-
currentExpression = directInitializer;
|
|
22233
|
+
return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
|
|
22529
22234
|
}
|
|
22235
|
+
return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
|
|
22530
22236
|
};
|
|
22531
22237
|
//#endregion
|
|
22532
22238
|
//#region src/plugin/rules/js-performance/js-min-max-loop.ts
|
|
@@ -24881,14 +24587,9 @@ const collectContextBindings = (programRoot) => {
|
|
|
24881
24587
|
}
|
|
24882
24588
|
return bindings;
|
|
24883
24589
|
};
|
|
24884
|
-
const isCreateContextBindingJsxName = (node, contextBindings
|
|
24590
|
+
const isCreateContextBindingJsxName = (node, contextBindings) => {
|
|
24885
24591
|
if (!isNodeOfType(node, "JSXIdentifier")) return false;
|
|
24886
|
-
if (!contextBindings.has(node.name))
|
|
24887
|
-
const symbol = scopes.symbolFor(node);
|
|
24888
|
-
if (!symbol || symbol.kind !== "import") return false;
|
|
24889
|
-
const importedName = getImportedName(symbol.declarationNode);
|
|
24890
|
-
return getImportSourceForName(node, node.name)?.toLowerCase().includes("context") === true && (node.name.endsWith("Context") || importedName?.endsWith("Context") === true);
|
|
24891
|
-
}
|
|
24592
|
+
if (!contextBindings.has(node.name)) return false;
|
|
24892
24593
|
const binding = findVariableInitializer(node, node.name);
|
|
24893
24594
|
if (!binding) return false;
|
|
24894
24595
|
return binding.scopeOwner.type === "Program";
|
|
@@ -24912,7 +24613,7 @@ const jsxNoConstructedContextValues = defineRule({
|
|
|
24912
24613
|
if (isTestlikeFile) return;
|
|
24913
24614
|
const nameNode = node.name;
|
|
24914
24615
|
const isLegacyProvider = isProviderMemberName(nameNode);
|
|
24915
|
-
const isReact19Shorthand = isCreateContextBindingJsxName(nameNode, contextBindings
|
|
24616
|
+
const isReact19Shorthand = isCreateContextBindingJsxName(nameNode, contextBindings);
|
|
24916
24617
|
if (!isLegacyProvider && !isReact19Shorthand) return;
|
|
24917
24618
|
if (!isInsideFunctionScope(node)) return;
|
|
24918
24619
|
for (const attribute of node.attributes) {
|
|
@@ -25183,29 +24884,6 @@ const buildSameFileJsxSlotPropRegistry = (program, memoRegistry, scopes) => {
|
|
|
25183
24884
|
return registry;
|
|
25184
24885
|
};
|
|
25185
24886
|
//#endregion
|
|
25186
|
-
//#region src/plugin/utils/has-custom-memo-comparator.ts
|
|
25187
|
-
const MEMO_CALLEE_NAMES$1 = new Set(["memo", "React.memo"]);
|
|
25188
|
-
const isIdentitySensitiveMemoComparator = (comparatorNode, scopes) => {
|
|
25189
|
-
if (isNodeOfType(comparatorNode, "Identifier")) {
|
|
25190
|
-
if (comparatorNode.name === "undefined") return scopes.isGlobalReference(comparatorNode);
|
|
25191
|
-
return getImportedNameFromModule(comparatorNode, comparatorNode.name, "react-redux") === "shallowEqual";
|
|
25192
|
-
}
|
|
25193
|
-
if (!isNodeOfType(comparatorNode, "MemberExpression") || comparatorNode.computed || !isNodeOfType(comparatorNode.object, "Identifier") || !isNodeOfType(comparatorNode.property, "Identifier") || comparatorNode.property.name !== "shallowEqual") return false;
|
|
25194
|
-
return isNamespaceImportFromModule$1(comparatorNode, comparatorNode.object.name, "react-redux");
|
|
25195
|
-
};
|
|
25196
|
-
const hasCustomMemoComparator = (openingName, scopes) => {
|
|
25197
|
-
if (!openingName || !isNodeOfType(openingName, "JSXIdentifier")) return false;
|
|
25198
|
-
const binding = findVariableInitializer(openingName, openingName.name);
|
|
25199
|
-
if (!binding || !binding.initializer) return false;
|
|
25200
|
-
const initializer = binding.initializer;
|
|
25201
|
-
if (!isNodeOfType(initializer, "CallExpression")) return false;
|
|
25202
|
-
const calleeName = flattenCalleeName(initializer.callee);
|
|
25203
|
-
if (calleeName === null || !MEMO_CALLEE_NAMES$1.has(calleeName)) return false;
|
|
25204
|
-
const comparatorNode = (initializer.arguments ?? [])[1];
|
|
25205
|
-
if (!comparatorNode) return false;
|
|
25206
|
-
return !isIdentitySensitiveMemoComparator(comparatorNode, scopes);
|
|
25207
|
-
};
|
|
25208
|
-
//#endregion
|
|
25209
24887
|
//#region src/plugin/utils/is-on-intrinsic-html-element.ts
|
|
25210
24888
|
const isJsxAttributeOnIntrinsicHtmlElement = (attribute) => {
|
|
25211
24889
|
const openingElement = attribute.parent;
|
|
@@ -25491,7 +25169,6 @@ const jsxNoJsxAsProp = defineRule({
|
|
|
25491
25169
|
const parentJsxOpening = node.parent;
|
|
25492
25170
|
const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
|
|
25493
25171
|
if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
|
|
25494
|
-
if (hasCustomMemoComparator(openingName, context.scopes)) return;
|
|
25495
25172
|
const openingSymbol = openingName && isNodeOfType(openingName, "JSXIdentifier") ? context.scopes.symbolFor(openingName) : null;
|
|
25496
25173
|
if (openingSymbol && isNodeOfType(node.name, "JSXIdentifier") && jsxSlotPropRegistry?.get(openingSymbol.id)?.has(node.name.name)) return;
|
|
25497
25174
|
if (isNodeOfType(node.name, "JSXIdentifier") && isSlotPropName(node.name.name)) return;
|
|
@@ -25511,6 +25188,19 @@ const jsxNoJsxAsProp = defineRule({
|
|
|
25511
25188
|
}
|
|
25512
25189
|
});
|
|
25513
25190
|
//#endregion
|
|
25191
|
+
//#region src/plugin/utils/has-custom-memo-comparator.ts
|
|
25192
|
+
const MEMO_CALLEE_NAMES = new Set(["memo", "React.memo"]);
|
|
25193
|
+
const hasCustomMemoComparator = (openingName) => {
|
|
25194
|
+
if (!openingName || !isNodeOfType(openingName, "JSXIdentifier")) return false;
|
|
25195
|
+
const binding = findVariableInitializer(openingName, openingName.name);
|
|
25196
|
+
if (!binding || !binding.initializer) return false;
|
|
25197
|
+
const initializer = binding.initializer;
|
|
25198
|
+
if (!isNodeOfType(initializer, "CallExpression")) return false;
|
|
25199
|
+
const calleeName = flattenCalleeName(initializer.callee);
|
|
25200
|
+
if (calleeName === null || !MEMO_CALLEE_NAMES.has(calleeName)) return false;
|
|
25201
|
+
return (initializer.arguments ?? []).length >= 2;
|
|
25202
|
+
};
|
|
25203
|
+
//#endregion
|
|
25514
25204
|
//#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop-tables.ts
|
|
25515
25205
|
const DATA_ARRAY_PROP_NAMES = new Set([
|
|
25516
25206
|
"data",
|
|
@@ -25869,7 +25559,7 @@ const jsxNoNewArrayAsProp = defineRule({
|
|
|
25869
25559
|
const parentJsxOpening = node.parent;
|
|
25870
25560
|
const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
|
|
25871
25561
|
if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
|
|
25872
|
-
if (hasCustomMemoComparator(openingName
|
|
25562
|
+
if (hasCustomMemoComparator(openingName)) return;
|
|
25873
25563
|
if (isNodeOfType(node.name, "JSXIdentifier") && isDataArrayPropName(node.name.name)) return;
|
|
25874
25564
|
if (!isInsideFunctionScope(node)) return;
|
|
25875
25565
|
const value = node.value;
|
|
@@ -26333,7 +26023,6 @@ const jsxNoNewFunctionAsProp = defineRule({
|
|
|
26333
26023
|
const parentJsxOpening = node.parent;
|
|
26334
26024
|
const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
|
|
26335
26025
|
if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
|
|
26336
|
-
if (hasCustomMemoComparator(openingName, context.scopes)) return;
|
|
26337
26026
|
if (isNodeOfType(node.name, "JSXIdentifier") && isOneShotHandlerName(node.name.name)) return;
|
|
26338
26027
|
if (!isInsideFunctionScope(node)) return;
|
|
26339
26028
|
const value = node.value;
|
|
@@ -26641,7 +26330,7 @@ const jsxNoNewObjectAsProp = defineRule({
|
|
|
26641
26330
|
const parentJsxOpening = node.parent;
|
|
26642
26331
|
const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
|
|
26643
26332
|
if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
|
|
26644
|
-
if (hasCustomMemoComparator(openingName
|
|
26333
|
+
if (hasCustomMemoComparator(openingName)) return;
|
|
26645
26334
|
if (!isInsideFunctionScope(node)) return;
|
|
26646
26335
|
if (!isNodeOfType(node.name, "JSXIdentifier")) return;
|
|
26647
26336
|
if (ALWAYS_FRESH_OBJECT_PROPS.has(node.name.name)) return;
|
|
@@ -29673,14 +29362,6 @@ const nextjsNoImgElement = defineRule({
|
|
|
29673
29362
|
});
|
|
29674
29363
|
//#endregion
|
|
29675
29364
|
//#region src/plugin/rules/nextjs/nextjs-no-native-script.ts
|
|
29676
|
-
const hasEnabledBooleanAttribute = (attributes, attributeName) => {
|
|
29677
|
-
const attribute = findJsxAttribute(attributes, attributeName);
|
|
29678
|
-
if (!attribute) return false;
|
|
29679
|
-
if (attribute.value === null) return true;
|
|
29680
|
-
if (isNodeOfType(attribute.value, "Literal")) return true;
|
|
29681
|
-
if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return false;
|
|
29682
|
-
return isNodeOfType(attribute.value.expression, "Literal") && Boolean(attribute.value.expression.value);
|
|
29683
|
-
};
|
|
29684
29365
|
const nextjsNoNativeScript = defineRule({
|
|
29685
29366
|
id: "nextjs-no-native-script",
|
|
29686
29367
|
title: "Plain script can block Next.js rendering",
|
|
@@ -29693,9 +29374,6 @@ const nextjsNoNativeScript = defineRule({
|
|
|
29693
29374
|
const typeAttribute = findJsxAttribute(node.attributes ?? [], "type");
|
|
29694
29375
|
const typeValue = isNodeOfType(typeAttribute?.value, "Literal") ? typeAttribute.value.value : null;
|
|
29695
29376
|
if (typeof typeValue === "string" && !EXECUTABLE_SCRIPT_TYPES.has(typeValue)) return;
|
|
29696
|
-
if (hasEnabledBooleanAttribute(node.attributes ?? [], "async")) return;
|
|
29697
|
-
if (hasEnabledBooleanAttribute(node.attributes ?? [], "defer")) return;
|
|
29698
|
-
if (typeValue === "module") return;
|
|
29699
29377
|
const hasSrcAttribute = Boolean(findJsxAttribute(node.attributes ?? [], "src"));
|
|
29700
29378
|
if (Boolean(findJsxAttribute(node.attributes ?? [], "dangerouslySetInnerHTML")) && !hasSrcAttribute) return;
|
|
29701
29379
|
context.report({
|
|
@@ -34788,19 +34466,6 @@ const noCascadingSetState = defineRetiredRule({
|
|
|
34788
34466
|
recommendation: "Retired: React batches synchronous state updates from one effect into the same follow-up commit, so setter count does not prove repeated redraws."
|
|
34789
34467
|
});
|
|
34790
34468
|
//#endregion
|
|
34791
|
-
//#region src/plugin/utils/get-react-use-callback-call.ts
|
|
34792
|
-
const getReactUseCallbackCall = (expression, scopes) => {
|
|
34793
|
-
const identifier = stripParenExpression(expression);
|
|
34794
|
-
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
34795
|
-
const symbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
34796
|
-
if (symbol?.kind !== "const" || !symbol.initializer) return null;
|
|
34797
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
34798
|
-
return isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", scopes, {
|
|
34799
|
-
allowGlobalReactNamespace: true,
|
|
34800
|
-
resolveNamedAliases: true
|
|
34801
|
-
}) ? initializer : null;
|
|
34802
|
-
};
|
|
34803
|
-
//#endregion
|
|
34804
34469
|
//#region src/plugin/rules/state-and-effects/utils/create-state-trigger-reachability.ts
|
|
34805
34470
|
const getRefCurrentSymbol = (node, context) => {
|
|
34806
34471
|
const expression = stripParenExpression(node);
|
|
@@ -35329,8 +34994,19 @@ const isBuiltinNamespaceCallee = (callee) => {
|
|
|
35329
34994
|
}
|
|
35330
34995
|
return false;
|
|
35331
34996
|
};
|
|
34997
|
+
const getReactUseCallbackSource = (reference, context) => {
|
|
34998
|
+
const identifier = reference.identifier;
|
|
34999
|
+
const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
|
|
35000
|
+
if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
|
|
35001
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
35002
|
+
if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
|
|
35003
|
+
allowGlobalReactNamespace: true,
|
|
35004
|
+
resolveNamedAliases: true
|
|
35005
|
+
})) return initializer;
|
|
35006
|
+
return null;
|
|
35007
|
+
};
|
|
35332
35008
|
const getDependencyStateRefs = (analysis, context, dependencyReference) => {
|
|
35333
|
-
const useCallbackCall =
|
|
35009
|
+
const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
|
|
35334
35010
|
if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
|
|
35335
35011
|
const dependencyList = useCallbackCall.arguments?.[1];
|
|
35336
35012
|
if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
|
|
@@ -38844,17 +38520,6 @@ const noDynamicImportPath = defineRule({
|
|
|
38844
38520
|
})
|
|
38845
38521
|
});
|
|
38846
38522
|
//#endregion
|
|
38847
|
-
//#region src/plugin/utils/get-require-call-source.ts
|
|
38848
|
-
const getRequireCallSource = (expression) => {
|
|
38849
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
38850
|
-
if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
|
|
38851
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
|
|
38852
|
-
if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
|
|
38853
|
-
const [firstArgument] = unwrappedExpression.arguments ?? [];
|
|
38854
|
-
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
38855
|
-
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
38856
|
-
};
|
|
38857
|
-
//#endregion
|
|
38858
38523
|
//#region src/plugin/rules/state-and-effects/utils/is-cleanup-return.ts
|
|
38859
38524
|
const ITERATOR_CALLBACK_METHOD_NAMES = new Set([
|
|
38860
38525
|
"each",
|
|
@@ -38999,16 +38664,7 @@ const collectDependencyStateSymbolIds = (effectNode, stateSymbolIds, scopes) =>
|
|
|
38999
38664
|
}
|
|
39000
38665
|
return dependencyStateSymbolIds;
|
|
39001
38666
|
};
|
|
39002
|
-
const
|
|
39003
|
-
const localFunction = resolveExactLocalFunction(expression, scopes);
|
|
39004
|
-
if (localFunction) return localFunction;
|
|
39005
|
-
const useCallbackCall = getReactUseCallbackCall(expression, scopes);
|
|
39006
|
-
if (!useCallbackCall) return null;
|
|
39007
|
-
const callback = useCallbackCall.arguments[0];
|
|
39008
|
-
if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
|
|
39009
|
-
return resolveExactLocalFunction(callback, scopes);
|
|
39010
|
-
};
|
|
39011
|
-
const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeStableCallbacks = true) => {
|
|
38667
|
+
const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
|
|
39012
38668
|
const analysisFunctions = new Set([effectCallback]);
|
|
39013
38669
|
const pendingFunctions = [effectCallback];
|
|
39014
38670
|
while (pendingFunctions.length > 0) {
|
|
@@ -39016,9 +38672,9 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeSta
|
|
|
39016
38672
|
if (!currentFunction || !isFunctionLike$1(currentFunction)) continue;
|
|
39017
38673
|
walkInsideStatementBlocks(currentFunction.body, (child) => {
|
|
39018
38674
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
39019
|
-
const invokedFunction =
|
|
38675
|
+
const invokedFunction = resolveExactLocalFunction(child.callee, scopes);
|
|
39020
38676
|
if (!invokedFunction || analysisFunctions.has(invokedFunction)) return;
|
|
39021
|
-
if (isFunctionLike$1(invokedFunction) &&
|
|
38677
|
+
if (isFunctionLike$1(invokedFunction) && invokedFunction.async) return;
|
|
39022
38678
|
analysisFunctions.add(invokedFunction);
|
|
39023
38679
|
pendingFunctions.push(invokedFunction);
|
|
39024
38680
|
});
|
|
@@ -39031,51 +38687,7 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
|
|
|
39031
38687
|
walkInsideStatementBlocks(analysisFunction.body, visitor);
|
|
39032
38688
|
}
|
|
39033
38689
|
};
|
|
39034
|
-
const
|
|
39035
|
-
if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier")) return null;
|
|
39036
|
-
const setterSymbol = resolveConstIdentifierAlias(node.callee, scopes, true);
|
|
39037
|
-
return setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) ?? null : null;
|
|
39038
|
-
};
|
|
39039
|
-
const collectStateWriteAnalysisFunctions = (effectCallback, scopes, setterSymbolIdToStateName) => {
|
|
39040
|
-
const ordinaryAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes, false);
|
|
39041
|
-
const fullAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes);
|
|
39042
|
-
const stableAnalysisFunctions = [...fullAnalysisFunctions].filter((analysisFunction) => !ordinaryAnalysisFunctions.has(analysisFunction));
|
|
39043
|
-
if (stableAnalysisFunctions.length === 0) return ordinaryAnalysisFunctions;
|
|
39044
|
-
let hasUnprovenObservableWork = stableAnalysisFunctions.some((analysisFunction) => {
|
|
39045
|
-
if (!isFunctionLike$1(analysisFunction)) return false;
|
|
39046
|
-
let hasExecutableDefault = false;
|
|
39047
|
-
for (const parameter of analysisFunction.params) {
|
|
39048
|
-
walkInsideStatementBlocks(parameter, (child) => {
|
|
39049
|
-
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;
|
|
39050
|
-
});
|
|
39051
|
-
if (hasExecutableDefault) return true;
|
|
39052
|
-
}
|
|
39053
|
-
return false;
|
|
39054
|
-
});
|
|
39055
|
-
const stableWrittenStateNames = /* @__PURE__ */ new Set();
|
|
39056
|
-
const allWrittenStateNames = /* @__PURE__ */ new Set();
|
|
39057
|
-
visitSynchronousFunctionBodies(fullAnalysisFunctions, (child) => {
|
|
39058
|
-
const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
|
|
39059
|
-
if (writtenStateName) allWrittenStateNames.add(writtenStateName);
|
|
39060
|
-
});
|
|
39061
|
-
visitSynchronousFunctionBodies(new Set(stableAnalysisFunctions), (child) => {
|
|
39062
|
-
if (hasUnprovenObservableWork) return;
|
|
39063
|
-
if (isNodeOfType(child, "CallExpression")) {
|
|
39064
|
-
const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
|
|
39065
|
-
if (writtenStateName) {
|
|
39066
|
-
stableWrittenStateNames.add(writtenStateName);
|
|
39067
|
-
return;
|
|
39068
|
-
}
|
|
39069
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
|
|
39070
|
-
if (invokedFunction && fullAnalysisFunctions.has(invokedFunction)) return;
|
|
39071
|
-
hasUnprovenObservableWork = true;
|
|
39072
|
-
return;
|
|
39073
|
-
}
|
|
39074
|
-
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;
|
|
39075
|
-
});
|
|
39076
|
-
return !hasUnprovenObservableWork && stableWrittenStateNames.size === 1 && allWrittenStateNames.size === 1 ? fullAnalysisFunctions : ordinaryAnalysisFunctions;
|
|
39077
|
-
};
|
|
39078
|
-
const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set(), additionalStateValues = /* @__PURE__ */ new Map()) => {
|
|
38690
|
+
const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
39079
38691
|
const unwrappedExpression = stripParenExpression(expression);
|
|
39080
38692
|
if (isNodeOfType(unwrappedExpression, "Literal")) {
|
|
39081
38693
|
const literalValue = unwrappedExpression.value;
|
|
@@ -39083,49 +38695,46 @@ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, vi
|
|
|
39083
38695
|
return null;
|
|
39084
38696
|
}
|
|
39085
38697
|
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
39086
|
-
|
|
39087
|
-
if (symbol?.id === stateSymbolId) return stateValue;
|
|
39088
|
-
if (symbol && additionalStateValues.has(symbol.id)) return additionalStateValues.get(symbol.id) ?? null;
|
|
38698
|
+
if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
|
|
39089
38699
|
if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
|
|
39090
|
-
if (unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression)) return { value: NaN };
|
|
39091
38700
|
const immutableSymbol = scopes.symbolFor(unwrappedExpression);
|
|
39092
38701
|
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;
|
|
39093
|
-
return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id)
|
|
38702
|
+
return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
|
|
39094
38703
|
}
|
|
39095
38704
|
if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
|
|
39096
38705
|
if (unwrappedExpression.operator === "void") return { value: void 0 };
|
|
39097
38706
|
if (unwrappedExpression.operator !== "!") return null;
|
|
39098
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38707
|
+
const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39099
38708
|
return argumentValue ? { value: !argumentValue.value } : null;
|
|
39100
38709
|
}
|
|
39101
38710
|
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
39102
38711
|
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")) {
|
|
39103
|
-
const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38712
|
+
const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39104
38713
|
return argumentValue ? { value: Boolean(argumentValue.value) } : null;
|
|
39105
38714
|
}
|
|
39106
38715
|
return null;
|
|
39107
38716
|
}
|
|
39108
38717
|
if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
|
|
39109
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38718
|
+
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39110
38719
|
if (!leftValue) return null;
|
|
39111
38720
|
if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
|
|
39112
38721
|
if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
|
|
39113
38722
|
if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
|
|
39114
|
-
return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38723
|
+
return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39115
38724
|
}
|
|
39116
38725
|
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
39117
|
-
const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38726
|
+
const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39118
38727
|
if (!testValue) return null;
|
|
39119
|
-
return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38728
|
+
return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39120
38729
|
}
|
|
39121
38730
|
if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
|
|
39122
|
-
const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38731
|
+
const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39123
38732
|
if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
|
|
39124
38733
|
return null;
|
|
39125
38734
|
}
|
|
39126
38735
|
if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
|
|
39127
|
-
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
39128
|
-
const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds
|
|
38736
|
+
const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
38737
|
+
const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
|
|
39129
38738
|
if (!leftValue || !rightValue) return null;
|
|
39130
38739
|
if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
|
|
39131
38740
|
const areEqual = leftValue.value === rightValue.value;
|
|
@@ -39164,7 +38773,8 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
|
|
|
39164
38773
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
39165
38774
|
if (!isNodeOfType(child, "CallExpression")) return;
|
|
39166
38775
|
if (!isNodeOfType(child.callee, "Identifier")) return;
|
|
39167
|
-
const
|
|
38776
|
+
const setterSymbol = resolveConstIdentifierAlias(child.callee, scopes, true);
|
|
38777
|
+
const stateName = setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) : void 0;
|
|
39168
38778
|
if (!stateName) return;
|
|
39169
38779
|
const writeInfo = stateWrites.get(stateName) ?? {
|
|
39170
38780
|
values: /* @__PURE__ */ new Set(),
|
|
@@ -39180,27 +38790,27 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
|
|
|
39180
38790
|
const isGlobalBooleanCall = (node, scopes) => {
|
|
39181
38791
|
return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
|
|
39182
38792
|
};
|
|
39183
|
-
const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes
|
|
38793
|
+
const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
|
|
39184
38794
|
let currentNode = workNode;
|
|
39185
38795
|
while (currentNode.parent) {
|
|
39186
38796
|
const parentNode = currentNode.parent;
|
|
39187
38797
|
if (isFunctionLike$1(parentNode)) break;
|
|
39188
38798
|
if (isNodeOfType(parentNode, "IfStatement")) {
|
|
39189
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue
|
|
38799
|
+
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
39190
38800
|
if (testValue) {
|
|
39191
38801
|
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
39192
38802
|
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
39193
38803
|
}
|
|
39194
38804
|
}
|
|
39195
38805
|
if (isNodeOfType(parentNode, "ConditionalExpression")) {
|
|
39196
|
-
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue
|
|
38806
|
+
const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
|
|
39197
38807
|
if (testValue) {
|
|
39198
38808
|
if (currentNode === parentNode.consequent && !testValue.value) return false;
|
|
39199
38809
|
if (currentNode === parentNode.alternate && testValue.value) return false;
|
|
39200
38810
|
}
|
|
39201
38811
|
}
|
|
39202
38812
|
if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
|
|
39203
|
-
const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue
|
|
38813
|
+
const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
|
|
39204
38814
|
if (leftValue) {
|
|
39205
38815
|
if (parentNode.operator === "&&" && !leftValue.value) return false;
|
|
39206
38816
|
if (parentNode.operator === "||" && leftValue.value) return false;
|
|
@@ -39212,7 +38822,7 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
|
|
|
39212
38822
|
if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
|
|
39213
38823
|
const earlierStatement = parentNode.body[index];
|
|
39214
38824
|
if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
|
|
39215
|
-
if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue
|
|
38825
|
+
if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
|
|
39216
38826
|
}
|
|
39217
38827
|
}
|
|
39218
38828
|
currentNode = parentNode;
|
|
@@ -39222,93 +38832,24 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
|
|
|
39222
38832
|
const isReaderWorkNode = (node, analysisFunctions, scopes) => {
|
|
39223
38833
|
if (isNodeOfType(node, "CallExpression")) {
|
|
39224
38834
|
if (isGlobalBooleanCall(node, scopes)) return false;
|
|
39225
|
-
const invokedFunction =
|
|
38835
|
+
const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
|
|
39226
38836
|
return !invokedFunction || !analysisFunctions.has(invokedFunction);
|
|
39227
38837
|
}
|
|
39228
38838
|
return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
|
|
39229
38839
|
};
|
|
39230
|
-
const
|
|
39231
|
-
if (
|
|
39232
|
-
const
|
|
39233
|
-
|
|
39234
|
-
return knownStateValues;
|
|
39235
|
-
};
|
|
39236
|
-
const buildInvokedFunctionParameterStateValues = (invokedFunction, invocation, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map(), callerParameterStateValues = /* @__PURE__ */ new Map()) => {
|
|
39237
|
-
if (!isFunctionLike$1(invokedFunction)) return /* @__PURE__ */ new Map();
|
|
39238
|
-
const parameterStateValues = /* @__PURE__ */ new Map();
|
|
39239
|
-
const callerKnownStateValues = mergeKnownStateValues(additionalStateValues, callerParameterStateValues);
|
|
39240
|
-
for (let parameterIndex = 0; parameterIndex < invokedFunction.params.length; parameterIndex += 1) {
|
|
39241
|
-
const rawParameter = invokedFunction.params[parameterIndex];
|
|
39242
|
-
const rawArgument = invocation.arguments[parameterIndex];
|
|
39243
|
-
if (rawArgument && isNodeOfType(rawArgument, "SpreadElement")) break;
|
|
39244
|
-
const parameter = isNodeOfType(rawParameter, "AssignmentPattern") ? rawParameter.left : rawParameter;
|
|
39245
|
-
if (!isNodeOfType(parameter, "Identifier")) continue;
|
|
39246
|
-
const parameterSymbol = scopes.symbolFor(parameter);
|
|
39247
|
-
if (!parameterSymbol) continue;
|
|
39248
|
-
let argumentValue = null;
|
|
39249
|
-
if (rawArgument) {
|
|
39250
|
-
argumentValue = readStaticEffectValue(rawArgument, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), callerKnownStateValues);
|
|
39251
|
-
if (argumentValue !== null && argumentValue.value === void 0 && isNodeOfType(rawParameter, "AssignmentPattern")) {
|
|
39252
|
-
const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
|
|
39253
|
-
argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
|
|
39254
|
-
}
|
|
39255
|
-
} else if (isNodeOfType(rawParameter, "AssignmentPattern")) {
|
|
39256
|
-
const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
|
|
39257
|
-
argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
|
|
39258
|
-
} else argumentValue = { value: void 0 };
|
|
39259
|
-
if (argumentValue) parameterStateValues.set(parameterSymbol.id, argumentValue);
|
|
39260
|
-
}
|
|
39261
|
-
return parameterStateValues;
|
|
39262
|
-
};
|
|
39263
|
-
const haveEqualParameterStateValues = (leftValues, rightValues) => {
|
|
39264
|
-
if (leftValues.size !== rightValues.size) return false;
|
|
39265
|
-
for (const [symbolId, leftValue] of leftValues) {
|
|
39266
|
-
const rightValue = rightValues.get(symbolId);
|
|
39267
|
-
if (!rightValue || !Object.is(leftValue.value, rightValue.value)) return false;
|
|
39268
|
-
}
|
|
39269
|
-
return true;
|
|
39270
|
-
};
|
|
39271
|
-
const canReaderWorkRunForStateValues = (readerEffect, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
|
|
39272
|
-
const pendingFrames = [{
|
|
39273
|
-
functionNode: readerEffect.callback,
|
|
39274
|
-
parameterStateValues: /* @__PURE__ */ new Map()
|
|
39275
|
-
}];
|
|
39276
|
-
const visitedParameterStateValues = /* @__PURE__ */ new Map();
|
|
39277
|
-
while (pendingFrames.length > 0) {
|
|
39278
|
-
const frame = pendingFrames.pop();
|
|
39279
|
-
if (!frame || !isFunctionLike$1(frame.functionNode)) continue;
|
|
39280
|
-
const functionParameterStateValues = visitedParameterStateValues.get(frame.functionNode) ?? [];
|
|
39281
|
-
if (functionParameterStateValues.some((previousValues) => haveEqualParameterStateValues(previousValues, frame.parameterStateValues))) continue;
|
|
39282
|
-
functionParameterStateValues.push(frame.parameterStateValues);
|
|
39283
|
-
visitedParameterStateValues.set(frame.functionNode, functionParameterStateValues);
|
|
39284
|
-
const knownStateValues = mergeKnownStateValues(additionalStateValues, frame.parameterStateValues);
|
|
38840
|
+
const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
|
|
38841
|
+
if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
|
|
38842
|
+
for (const writtenValue of writeInfo.values) {
|
|
38843
|
+
const stateValue = { value: writtenValue };
|
|
39285
38844
|
let didFindReachableWork = false;
|
|
39286
|
-
|
|
39287
|
-
if (didFindReachableWork) return;
|
|
39288
|
-
if (
|
|
39289
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
|
|
39290
|
-
if (invokedFunction && readerEffect.analysisFunctions.has(invokedFunction)) {
|
|
39291
|
-
if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) pendingFrames.push({
|
|
39292
|
-
functionNode: invokedFunction,
|
|
39293
|
-
parameterStateValues: buildInvokedFunctionParameterStateValues(invokedFunction, child, stateSymbolId, stateValue, scopes, additionalStateValues, frame.parameterStateValues)
|
|
39294
|
-
});
|
|
39295
|
-
return;
|
|
39296
|
-
}
|
|
39297
|
-
}
|
|
39298
|
-
if (isReaderWorkNode(child, readerEffect.analysisFunctions, scopes) && isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) didFindReachableWork = true;
|
|
38845
|
+
visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
|
|
38846
|
+
if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
|
|
38847
|
+
if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
|
|
39299
38848
|
});
|
|
39300
38849
|
if (didFindReachableWork) return true;
|
|
39301
38850
|
}
|
|
39302
38851
|
return false;
|
|
39303
38852
|
};
|
|
39304
|
-
const canStateWriteReachReaderWork = (writtenStateName, writerEffect, readerEffect, stateSymbolIds, scopes) => {
|
|
39305
|
-
const writeInfo = writerEffect.stateWrites.get(writtenStateName);
|
|
39306
|
-
const stateSymbolId = stateSymbolIds.get(writtenStateName);
|
|
39307
|
-
if (!writeInfo || stateSymbolId === void 0) return true;
|
|
39308
|
-
if (writeInfo.hasUnknownValue) return true;
|
|
39309
|
-
for (const writtenValue of writeInfo.values) if (canReaderWorkRunForStateValues(readerEffect, stateSymbolId, { value: writtenValue }, scopes)) return true;
|
|
39310
|
-
return false;
|
|
39311
|
-
};
|
|
39312
38853
|
const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
|
|
39313
38854
|
const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
|
|
39314
38855
|
"clear",
|
|
@@ -39319,499 +38860,26 @@ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
|
|
|
39319
38860
|
"keys",
|
|
39320
38861
|
"values"
|
|
39321
38862
|
]);
|
|
39322
|
-
const
|
|
39323
|
-
|
|
39324
|
-
|
|
39325
|
-
"
|
|
39326
|
-
|
|
39327
|
-
|
|
39328
|
-
|
|
39329
|
-
["got", new Set(["got"])],
|
|
39330
|
-
["wretch", new Set(["wretch"])],
|
|
39331
|
-
["ofetch", new Set(["ofetch"])],
|
|
39332
|
-
["setTimeout", new Set([
|
|
39333
|
-
"node:timers",
|
|
39334
|
-
"node:timers/promises",
|
|
39335
|
-
"timers",
|
|
39336
|
-
"timers/promises"
|
|
39337
|
-
])],
|
|
39338
|
-
["setInterval", new Set([
|
|
39339
|
-
"node:timers",
|
|
39340
|
-
"node:timers/promises",
|
|
39341
|
-
"timers",
|
|
39342
|
-
"timers/promises"
|
|
39343
|
-
])]
|
|
39344
|
-
]);
|
|
39345
|
-
const EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES = new Map([
|
|
39346
|
-
["axios", "axios"],
|
|
39347
|
-
["cross-fetch", "fetch"],
|
|
39348
|
-
["got", "got"],
|
|
39349
|
-
["ky", "ky"],
|
|
39350
|
-
["node-fetch", "fetch"],
|
|
39351
|
-
["wretch", "wretch"]
|
|
39352
|
-
]);
|
|
39353
|
-
const EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES = new Set([
|
|
39354
|
-
"axios",
|
|
39355
|
-
"cross-fetch",
|
|
39356
|
-
"got",
|
|
39357
|
-
"ky",
|
|
39358
|
-
"node-fetch",
|
|
39359
|
-
"ofetch",
|
|
39360
|
-
"undici",
|
|
39361
|
-
"wretch"
|
|
39362
|
-
]);
|
|
39363
|
-
const EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES = new Set([
|
|
39364
|
-
"axios",
|
|
39365
|
-
"got",
|
|
39366
|
-
"ky",
|
|
39367
|
-
"ofetch",
|
|
39368
|
-
"wretch"
|
|
39369
|
-
]);
|
|
39370
|
-
const EXTERNAL_SYNC_HTTP_METHOD_NAMES = new Set([
|
|
39371
|
-
...EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES,
|
|
39372
|
-
"fetch",
|
|
39373
|
-
"patch",
|
|
39374
|
-
"post",
|
|
39375
|
-
"put",
|
|
39376
|
-
"request"
|
|
39377
|
-
]);
|
|
39378
|
-
const TANSTACK_QUERY_CLIENT_MODULE_SOURCES = new Set(["@tanstack/query-core", "@tanstack/react-query"]);
|
|
39379
|
-
const TANSTACK_QUERY_EXTERNAL_SYNC_METHOD_NAMES = new Set(["fetchQuery", "prefetchQuery"]);
|
|
39380
|
-
const EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES = new Set([
|
|
39381
|
-
...EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS,
|
|
39382
|
-
...SOCKET_CONSTRUCTOR_NAMES_REQUIRING_CLEANUP,
|
|
39383
|
-
"EventTarget",
|
|
39384
|
-
"XMLHttpRequest"
|
|
39385
|
-
]);
|
|
39386
|
-
const getDestructuredBindingDepth = (bindingIdentifier) => {
|
|
39387
|
-
let bindingNode = bindingIdentifier;
|
|
39388
|
-
let depth = 0;
|
|
39389
|
-
while (true) {
|
|
39390
|
-
if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
|
|
39391
|
-
const property = bindingNode.parent;
|
|
39392
|
-
if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return depth;
|
|
39393
|
-
depth += 1;
|
|
39394
|
-
bindingNode = property.parent;
|
|
39395
|
-
}
|
|
39396
|
-
};
|
|
39397
|
-
const isMutatedMemberExpression = (rawMemberExpression) => {
|
|
39398
|
-
let expression = findTransparentExpressionRoot(rawMemberExpression);
|
|
39399
|
-
while (expression.parent) {
|
|
39400
|
-
const parent = expression.parent;
|
|
39401
|
-
if (isNodeOfType(parent, "MemberExpression") && parent.object === expression) {
|
|
39402
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39403
|
-
continue;
|
|
39404
|
-
}
|
|
39405
|
-
if (isNodeOfType(parent, "Property") && parent.value === expression && isNodeOfType(parent.parent, "ObjectPattern")) {
|
|
39406
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39407
|
-
continue;
|
|
39408
|
-
}
|
|
39409
|
-
if (isNodeOfType(parent, "ObjectPattern") && parent.properties.some((property) => property === expression)) {
|
|
39410
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39411
|
-
continue;
|
|
39412
|
-
}
|
|
39413
|
-
if (isNodeOfType(parent, "ArrayPattern") && parent.elements.some((element) => element === expression)) {
|
|
39414
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39415
|
-
continue;
|
|
39416
|
-
}
|
|
39417
|
-
if (isNodeOfType(parent, "RestElement") && parent.argument === expression || isNodeOfType(parent, "AssignmentPattern") && parent.left === expression) {
|
|
39418
|
-
expression = findTransparentExpressionRoot(parent);
|
|
39419
|
-
continue;
|
|
39420
|
-
}
|
|
39421
|
-
break;
|
|
39422
|
-
}
|
|
39423
|
-
const parent = expression.parent;
|
|
39424
|
-
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);
|
|
39425
|
-
};
|
|
39426
|
-
const isReactHookDependencyArgument = (expression, scopes) => {
|
|
39427
|
-
const call = expression.parent;
|
|
39428
|
-
return Boolean(isNodeOfType(call, "CallExpression") && call.arguments[1] === expression && isReactApiCall(call, HOOKS_WITH_DEPS, scopes, {
|
|
39429
|
-
allowGlobalReactNamespace: true,
|
|
39430
|
-
allowUnboundBareCalls: true,
|
|
39431
|
-
resolveNamedAliases: true
|
|
39432
|
-
}));
|
|
39433
|
-
};
|
|
39434
|
-
const isPlainDependencyObjectExpression = (expression) => isNodeOfType(expression, "ObjectExpression") && expression.properties.every((property) => isNodeOfType(property, "Property") && property.kind === "init" && !property.computed && !property.method);
|
|
39435
|
-
const getContainingReactDependencyValue = (rawExpression, containerKind) => {
|
|
39436
|
-
const expression = findTransparentExpressionRoot(rawExpression);
|
|
39437
|
-
const parent = expression.parent;
|
|
39438
|
-
if (isNodeOfType(parent, "ArrayExpression")) return {
|
|
39439
|
-
expression: parent,
|
|
39440
|
-
containerKind: "array"
|
|
39441
|
-
};
|
|
39442
|
-
if (containerKind === "array" && isNodeOfType(parent, "SpreadElement") && parent.argument === expression && isNodeOfType(parent.parent, "ArrayExpression")) return {
|
|
39443
|
-
expression: parent.parent,
|
|
39444
|
-
containerKind: "array"
|
|
39445
|
-
};
|
|
39446
|
-
if (isNodeOfType(parent, "Property") && (parent.value === expression || parent.shorthand && parent.key === expression) && parent.parent && isPlainDependencyObjectExpression(parent.parent)) return {
|
|
39447
|
-
expression: parent.parent,
|
|
39448
|
-
containerKind: "object"
|
|
39449
|
-
};
|
|
39450
|
-
return null;
|
|
39451
|
-
};
|
|
39452
|
-
const getFullArrayRestCopySymbol = (initializer, scopes) => {
|
|
39453
|
-
const declaration = initializer.parent;
|
|
39454
|
-
if (!isNodeOfType(declaration, "VariableDeclarator") || declaration.init !== initializer || !isNodeOfType(declaration.id, "ArrayPattern") || declaration.id.elements.length !== 1) return null;
|
|
39455
|
-
const restElement = declaration.id.elements[0];
|
|
39456
|
-
if (!isNodeOfType(restElement, "RestElement") || !isNodeOfType(restElement.argument, "Identifier")) return null;
|
|
39457
|
-
const symbol = scopes.symbolFor(restElement.argument);
|
|
39458
|
-
return symbol?.kind === "const" && symbol.declarationNode === declaration && symbol.references.every((reference) => reference.flag === "read") ? symbol : null;
|
|
39459
|
-
};
|
|
39460
|
-
const isReactDependencyArrayReference = (initialExpression, scopes) => {
|
|
39461
|
-
const pendingPaths = [{
|
|
39462
|
-
expression: initialExpression,
|
|
39463
|
-
containerKind: null
|
|
39464
|
-
}];
|
|
39465
|
-
const visitedExpressions = /* @__PURE__ */ new Set();
|
|
39466
|
-
let didFindReactDependencyArgument = false;
|
|
39467
|
-
while (pendingPaths.length > 0) {
|
|
39468
|
-
const pendingPath = pendingPaths.pop();
|
|
39469
|
-
if (!pendingPath) continue;
|
|
39470
|
-
const expression = findTransparentExpressionRoot(pendingPath.expression);
|
|
39471
|
-
if (visitedExpressions.has(expression)) continue;
|
|
39472
|
-
visitedExpressions.add(expression);
|
|
39473
|
-
if (pendingPath.containerKind === "array" && isReactHookDependencyArgument(expression, scopes)) {
|
|
39474
|
-
didFindReactDependencyArgument = true;
|
|
39475
|
-
continue;
|
|
39476
|
-
}
|
|
39477
|
-
const containingValue = getContainingReactDependencyValue(expression, pendingPath.containerKind);
|
|
39478
|
-
if (containingValue) {
|
|
39479
|
-
pendingPaths.push(containingValue);
|
|
39480
|
-
continue;
|
|
39481
|
-
}
|
|
39482
|
-
const declaration = expression.parent;
|
|
39483
|
-
if (isNodeOfType(declaration, "VariableDeclarator") && declaration.init === expression && isNodeOfType(declaration.id, "Identifier")) {
|
|
39484
|
-
const symbol = scopes.symbolFor(declaration.id);
|
|
39485
|
-
if (!symbol || getDirectUnreassignedInitializer(symbol) !== expression) return false;
|
|
39486
|
-
if (symbol.references.length === 0) return false;
|
|
39487
|
-
for (const reference of symbol.references) pendingPaths.push({
|
|
39488
|
-
expression: reference.identifier,
|
|
39489
|
-
containerKind: pendingPath.containerKind
|
|
39490
|
-
});
|
|
39491
|
-
continue;
|
|
39492
|
-
}
|
|
39493
|
-
if (pendingPath.containerKind === "array") {
|
|
39494
|
-
const restCopySymbol = getFullArrayRestCopySymbol(expression, scopes);
|
|
39495
|
-
if (restCopySymbol) {
|
|
39496
|
-
if (restCopySymbol.references.length === 0) return false;
|
|
39497
|
-
for (const reference of restCopySymbol.references) pendingPaths.push({
|
|
39498
|
-
expression: reference.identifier,
|
|
39499
|
-
containerKind: "array"
|
|
39500
|
-
});
|
|
39501
|
-
continue;
|
|
39502
|
-
}
|
|
39503
|
-
}
|
|
39504
|
-
return false;
|
|
39505
|
-
}
|
|
39506
|
-
return didFindReactDependencyArgument;
|
|
39507
|
-
};
|
|
39508
|
-
const hasSafeReceiverAliases = (rootSymbols, scopes) => {
|
|
39509
|
-
const pendingSymbols = [...rootSymbols];
|
|
39510
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
39511
|
-
while (pendingSymbols.length > 0) {
|
|
39512
|
-
const symbol = pendingSymbols.pop();
|
|
39513
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) continue;
|
|
39514
|
-
visitedSymbolIds.add(symbol.id);
|
|
39515
|
-
for (const reference of symbol.references) {
|
|
39516
|
-
const expression = findTransparentExpressionRoot(reference.identifier);
|
|
39517
|
-
const container = expression.parent;
|
|
39518
|
-
if (isReactDependencyArrayReference(expression, scopes)) continue;
|
|
39519
|
-
if (isNodeOfType(container, "MemberExpression") && container.object === expression) {
|
|
39520
|
-
if (isMutatedMemberExpression(container)) return false;
|
|
39521
|
-
continue;
|
|
39522
|
-
}
|
|
39523
|
-
if (isNodeOfType(container, "CallExpression") && container.callee === expression) continue;
|
|
39524
|
-
if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "ObjectPattern")) continue;
|
|
39525
|
-
if (isNodeOfType(container, "AssignmentExpression") && container.right === expression && isNodeOfType(container.left, "ObjectPattern")) continue;
|
|
39526
|
-
if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "Identifier")) {
|
|
39527
|
-
const aliasSymbol = scopes.symbolFor(container.id);
|
|
39528
|
-
const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
|
|
39529
|
-
if (!aliasSymbol || aliasInitializer !== expression) return false;
|
|
39530
|
-
pendingSymbols.push(aliasSymbol);
|
|
39531
|
-
continue;
|
|
39532
|
-
}
|
|
39533
|
-
return false;
|
|
38863
|
+
const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
|
|
38864
|
+
if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
|
|
38865
|
+
if (isNodeOfType(returnedValue, "CallExpression")) {
|
|
38866
|
+
if (isNodeOfType(returnedValue.callee, "Identifier")) {
|
|
38867
|
+
const setterSymbol = resolveConstIdentifierAlias(returnedValue.callee, scopes, true);
|
|
38868
|
+
if (setterToStateName.has(returnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
|
|
38869
|
+
if (isSetterIdentifier(returnedValue.callee.name)) return true;
|
|
39534
38870
|
}
|
|
38871
|
+
return isCleanupReturn(returnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
|
|
39535
38872
|
}
|
|
39536
|
-
return true;
|
|
39537
|
-
};
|
|
39538
|
-
const hasProvenGlobalNamespaceReference = (expression, namespaceNames, scopes) => {
|
|
39539
|
-
for (const namespaceName of namespaceNames) if (isProvenGlobalNamespaceReference(expression, namespaceName, scopes)) return true;
|
|
38873
|
+
if (isNodeOfType(returnedValue, "Identifier")) return true;
|
|
39540
38874
|
return false;
|
|
39541
38875
|
};
|
|
39542
|
-
const
|
|
39543
|
-
|
|
39544
|
-
|
|
39545
|
-
if (
|
|
39546
|
-
return
|
|
39547
|
-
};
|
|
39548
|
-
const getUnshadowedRequireSource = (rawExpression, scopes) => {
|
|
39549
|
-
let expression = stripParenExpression(rawExpression);
|
|
39550
|
-
while (isNodeOfType(expression, "MemberExpression")) expression = stripParenExpression(expression.object);
|
|
39551
|
-
if (!isNodeOfType(expression, "CallExpression") || !isNodeOfType(expression.callee, "Identifier") || expression.callee.name !== "require" || !scopes.isGlobalReference(expression.callee)) return null;
|
|
39552
|
-
return getRequireCallSource(rawExpression);
|
|
39553
|
-
};
|
|
39554
|
-
const resolveExternalModuleBinding = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set(), ignoreCommonJsMutation = false) => {
|
|
39555
|
-
let expression = stripParenExpression(rawExpression);
|
|
39556
|
-
let outermostExportedName = null;
|
|
39557
|
-
let exportedPropertyDepth = 0;
|
|
39558
|
-
let resolvedBinding = null;
|
|
39559
|
-
let isCommonJsModuleObject = false;
|
|
39560
|
-
const traversedSymbolIds = new Set(visitedSymbolIds);
|
|
39561
|
-
const commonJsModuleObjectSymbols = /* @__PURE__ */ new Set();
|
|
39562
|
-
while (true) {
|
|
39563
|
-
if (isNodeOfType(expression, "MemberExpression")) {
|
|
39564
|
-
const propertyName = getStaticPropertyName(expression);
|
|
39565
|
-
if (propertyName === null) return null;
|
|
39566
|
-
outermostExportedName ??= propertyName;
|
|
39567
|
-
exportedPropertyDepth += 1;
|
|
39568
|
-
expression = stripParenExpression(expression.object);
|
|
39569
|
-
continue;
|
|
39570
|
-
}
|
|
39571
|
-
const requireSource = getUnshadowedRequireSource(expression, scopes);
|
|
39572
|
-
if (requireSource !== null) {
|
|
39573
|
-
resolvedBinding = {
|
|
39574
|
-
source: requireSource,
|
|
39575
|
-
exportedName: null,
|
|
39576
|
-
isModuleObject: true
|
|
39577
|
-
};
|
|
39578
|
-
isCommonJsModuleObject = true;
|
|
39579
|
-
break;
|
|
39580
|
-
}
|
|
39581
|
-
if (!isNodeOfType(expression, "Identifier")) return null;
|
|
39582
|
-
const symbol = scopes.symbolFor(expression);
|
|
39583
|
-
if (!symbol || traversedSymbolIds.has(symbol.id)) return null;
|
|
39584
|
-
if (symbol.kind === "import") {
|
|
39585
|
-
const importDeclaration = symbol.declarationNode.parent;
|
|
39586
|
-
if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string") return null;
|
|
39587
|
-
if (isNodeOfType(symbol.declarationNode, "ImportSpecifier")) resolvedBinding = {
|
|
39588
|
-
source: importDeclaration.source.value,
|
|
39589
|
-
exportedName: getImportedName(symbol.declarationNode) ?? null,
|
|
39590
|
-
isModuleObject: false
|
|
39591
|
-
};
|
|
39592
|
-
else if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) resolvedBinding = {
|
|
39593
|
-
source: importDeclaration.source.value,
|
|
39594
|
-
exportedName: "default",
|
|
39595
|
-
isModuleObject: false
|
|
39596
|
-
};
|
|
39597
|
-
else if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) resolvedBinding = {
|
|
39598
|
-
source: importDeclaration.source.value,
|
|
39599
|
-
exportedName: null,
|
|
39600
|
-
isModuleObject: true
|
|
39601
|
-
};
|
|
39602
|
-
break;
|
|
39603
|
-
}
|
|
39604
|
-
const importEqualsSource = getTypeScriptImportEqualsSource(symbol);
|
|
39605
|
-
if (importEqualsSource !== null) {
|
|
39606
|
-
resolvedBinding = {
|
|
39607
|
-
source: importEqualsSource,
|
|
39608
|
-
exportedName: null,
|
|
39609
|
-
isModuleObject: true
|
|
39610
|
-
};
|
|
39611
|
-
isCommonJsModuleObject = true;
|
|
39612
|
-
commonJsModuleObjectSymbols.add(symbol);
|
|
39613
|
-
break;
|
|
39614
|
-
}
|
|
39615
|
-
const directInitializer = getDirectUnreassignedInitializer(symbol);
|
|
39616
|
-
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
39617
|
-
const initializer = directInitializer ?? (destructuredPropertyName !== null && symbol.kind === "const" && isNodeOfType(symbol.declarationNode, "VariableDeclarator") ? symbol.declarationNode.init : null);
|
|
39618
|
-
if (!initializer) return null;
|
|
39619
|
-
traversedSymbolIds.add(symbol.id);
|
|
39620
|
-
outermostExportedName ??= destructuredPropertyName;
|
|
39621
|
-
if (destructuredPropertyName !== null) exportedPropertyDepth += getDestructuredBindingDepth(symbol.bindingIdentifier);
|
|
39622
|
-
const unwrappedInitializer = stripParenExpression(initializer);
|
|
39623
|
-
if (destructuredPropertyName === null && !isNodeOfType(unwrappedInitializer, "MemberExpression")) commonJsModuleObjectSymbols.add(symbol);
|
|
39624
|
-
expression = unwrappedInitializer;
|
|
39625
|
-
}
|
|
39626
|
-
if (!resolvedBinding) return null;
|
|
39627
|
-
if (!ignoreCommonJsMutation && isCommonJsModuleObject && !hasSafeReceiverAliases(commonJsModuleObjectSymbols, scopes)) return null;
|
|
39628
|
-
if (outermostExportedName === null) return resolvedBinding;
|
|
39629
|
-
if (!resolvedBinding.isModuleObject || exportedPropertyDepth !== 1) return null;
|
|
39630
|
-
return {
|
|
39631
|
-
source: resolvedBinding.source,
|
|
39632
|
-
exportedName: outermostExportedName,
|
|
39633
|
-
isModuleObject: false
|
|
39634
|
-
};
|
|
39635
|
-
};
|
|
39636
|
-
const getCanonicalExternalSyncExportName = (binding) => {
|
|
39637
|
-
if (binding.exportedName && binding.exportedName !== "default") return binding.exportedName;
|
|
39638
|
-
return EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.get(binding.source) ?? null;
|
|
39639
|
-
};
|
|
39640
|
-
const isKnownExternalSyncDirectModuleBinding = (binding) => {
|
|
39641
|
-
const exportedName = getCanonicalExternalSyncExportName(binding);
|
|
39642
|
-
return Boolean(exportedName && EXTERNAL_SYNC_DIRECT_IMPORT_SOURCES.get(exportedName)?.has(binding.source));
|
|
39643
|
-
};
|
|
39644
|
-
const DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES = new Set([
|
|
39645
|
-
"Array",
|
|
39646
|
-
"BigInt",
|
|
39647
|
-
"Boolean",
|
|
39648
|
-
"Date",
|
|
39649
|
-
"Number",
|
|
39650
|
-
"String",
|
|
39651
|
-
"Symbol"
|
|
39652
|
-
]);
|
|
39653
|
-
const DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES = new Set([
|
|
39654
|
-
"Array",
|
|
39655
|
-
"Boolean",
|
|
39656
|
-
"Date",
|
|
39657
|
-
"Map",
|
|
39658
|
-
"Number",
|
|
39659
|
-
"Promise",
|
|
39660
|
-
"RegExp",
|
|
39661
|
-
"Set",
|
|
39662
|
-
"String",
|
|
39663
|
-
"URL",
|
|
39664
|
-
"URLSearchParams",
|
|
39665
|
-
"WeakMap",
|
|
39666
|
-
"WeakSet"
|
|
39667
|
-
]);
|
|
39668
|
-
const PROMISE_STATIC_RESULT_METHOD_NAMES = new Set([
|
|
39669
|
-
"all",
|
|
39670
|
-
"allSettled",
|
|
39671
|
-
"any",
|
|
39672
|
-
"race",
|
|
39673
|
-
"reject",
|
|
39674
|
-
"resolve",
|
|
39675
|
-
"withResolvers"
|
|
39676
|
-
]);
|
|
39677
|
-
const returnsOnlyNonCleanupValues = (functionNode, setterToStateName, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
|
|
39678
|
-
if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return false;
|
|
39679
|
-
const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
|
|
39680
|
-
const isNonCleanupValue = (returnValue) => {
|
|
39681
|
-
const expression = stripParenExpression(returnValue);
|
|
39682
|
-
if (isNodeOfType(expression, "Identifier") && expression.name === "undefined" && scopes.isGlobalReference(expression)) return true;
|
|
39683
|
-
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;
|
|
39684
|
-
if (isNodeOfType(expression, "ConditionalExpression")) return isNonCleanupValue(expression.consequent) && isNonCleanupValue(expression.alternate);
|
|
39685
|
-
if (isNodeOfType(expression, "LogicalExpression")) return isNonCleanupValue(expression.left) && isNonCleanupValue(expression.right);
|
|
39686
|
-
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
39687
|
-
const finalExpression = expression.expressions.at(-1);
|
|
39688
|
-
return Boolean(finalExpression && isNonCleanupValue(finalExpression));
|
|
39689
|
-
}
|
|
39690
|
-
if (isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
|
|
39691
|
-
if (!isNodeOfType(expression, "CallExpression")) return false;
|
|
39692
|
-
if (isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
|
|
39693
|
-
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;
|
|
39694
|
-
if (isNodeOfType(expression.callee, "Identifier") && setterToStateName.has(expression.callee.name)) return true;
|
|
39695
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
|
|
39696
|
-
return Boolean(invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes, nextVisitedFunctions));
|
|
39697
|
-
};
|
|
39698
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNonCleanupValue(functionNode.body);
|
|
39699
|
-
let returnsOnlyNonCleanup = true;
|
|
39700
|
-
walkAst(functionNode.body, (child) => {
|
|
39701
|
-
if (!returnsOnlyNonCleanup) return false;
|
|
39702
|
-
if (child !== functionNode.body && isFunctionLike$1(child)) return false;
|
|
39703
|
-
if (!isNodeOfType(child, "ReturnStatement")) return;
|
|
39704
|
-
if (child.argument && !isNonCleanupValue(child.argument)) {
|
|
39705
|
-
returnsOnlyNonCleanup = false;
|
|
39706
|
-
return false;
|
|
39707
|
-
}
|
|
39708
|
-
});
|
|
39709
|
-
return returnsOnlyNonCleanup;
|
|
39710
|
-
};
|
|
39711
|
-
const getResolvedFunctionCleanupReturnProof = (functionNode, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
|
|
39712
|
-
if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return {
|
|
39713
|
-
hasCleanup: false,
|
|
39714
|
-
isValid: false
|
|
39715
|
-
};
|
|
39716
|
-
const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
|
|
39717
|
-
const getCleanupReturnProof = (returnValue) => {
|
|
39718
|
-
const expression = stripParenExpression(returnValue);
|
|
39719
|
-
if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression")) return {
|
|
39720
|
-
hasCleanup: true,
|
|
39721
|
-
isValid: true
|
|
39722
|
-
};
|
|
39723
|
-
if (isNodeOfType(expression, "Identifier")) {
|
|
39724
|
-
if (expression.name === "undefined" && scopes.isGlobalReference(expression)) return {
|
|
39725
|
-
hasCleanup: false,
|
|
39726
|
-
isValid: true
|
|
39727
|
-
};
|
|
39728
|
-
const localFunction = resolveExactLocalFunction(expression, scopes);
|
|
39729
|
-
return {
|
|
39730
|
-
hasCleanup: Boolean(localFunction),
|
|
39731
|
-
isValid: Boolean(localFunction)
|
|
39732
|
-
};
|
|
39733
|
-
}
|
|
39734
|
-
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return {
|
|
39735
|
-
hasCleanup: false,
|
|
39736
|
-
isValid: true
|
|
39737
|
-
};
|
|
39738
|
-
if (isNodeOfType(expression, "ConditionalExpression")) {
|
|
39739
|
-
const consequentProof = getCleanupReturnProof(expression.consequent);
|
|
39740
|
-
const alternateProof = getCleanupReturnProof(expression.alternate);
|
|
39741
|
-
return {
|
|
39742
|
-
hasCleanup: consequentProof.hasCleanup || alternateProof.hasCleanup,
|
|
39743
|
-
isValid: consequentProof.isValid && alternateProof.isValid
|
|
39744
|
-
};
|
|
39745
|
-
}
|
|
39746
|
-
if (isNodeOfType(expression, "LogicalExpression")) {
|
|
39747
|
-
const leftProof = getCleanupReturnProof(expression.left);
|
|
39748
|
-
const rightProof = getCleanupReturnProof(expression.right);
|
|
39749
|
-
return {
|
|
39750
|
-
hasCleanup: leftProof.hasCleanup || rightProof.hasCleanup,
|
|
39751
|
-
isValid: leftProof.isValid && rightProof.isValid
|
|
39752
|
-
};
|
|
39753
|
-
}
|
|
39754
|
-
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
39755
|
-
const finalExpression = expression.expressions.at(-1);
|
|
39756
|
-
return finalExpression ? getCleanupReturnProof(finalExpression) : {
|
|
39757
|
-
hasCleanup: false,
|
|
39758
|
-
isValid: false
|
|
39759
|
-
};
|
|
39760
|
-
}
|
|
39761
|
-
if (!isNodeOfType(expression, "CallExpression")) return {
|
|
39762
|
-
hasCleanup: false,
|
|
39763
|
-
isValid: false
|
|
39764
|
-
};
|
|
39765
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
|
|
39766
|
-
return invokedFunction ? getResolvedFunctionCleanupReturnProof(invokedFunction, scopes, nextVisitedFunctions) : {
|
|
39767
|
-
hasCleanup: false,
|
|
39768
|
-
isValid: false
|
|
39769
|
-
};
|
|
39770
|
-
};
|
|
39771
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return getCleanupReturnProof(functionNode.body);
|
|
39772
|
-
let cleanupReturnProof = {
|
|
39773
|
-
hasCleanup: false,
|
|
39774
|
-
isValid: true
|
|
39775
|
-
};
|
|
39776
|
-
walkAst(functionNode.body, (child) => {
|
|
39777
|
-
if (!cleanupReturnProof.isValid) return false;
|
|
39778
|
-
if (child !== functionNode.body && isFunctionLike$1(child)) return false;
|
|
39779
|
-
if (!isNodeOfType(child, "ReturnStatement")) return;
|
|
39780
|
-
if (!child.argument) return;
|
|
39781
|
-
const returnProof = getCleanupReturnProof(child.argument);
|
|
39782
|
-
if (!returnProof.isValid) {
|
|
39783
|
-
cleanupReturnProof = {
|
|
39784
|
-
hasCleanup: false,
|
|
39785
|
-
isValid: false
|
|
39786
|
-
};
|
|
39787
|
-
return false;
|
|
39788
|
-
}
|
|
39789
|
-
cleanupReturnProof.hasCleanup ||= returnProof.hasCleanup;
|
|
39790
|
-
});
|
|
39791
|
-
return cleanupReturnProof;
|
|
39792
|
-
};
|
|
39793
|
-
const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
|
|
39794
|
-
const unwrappedReturnedValue = stripParenExpression(returnedValue);
|
|
39795
|
-
if (isNodeOfType(unwrappedReturnedValue, "ArrowFunctionExpression") || isNodeOfType(unwrappedReturnedValue, "FunctionExpression")) return true;
|
|
39796
|
-
if (isNodeOfType(unwrappedReturnedValue, "CallExpression")) {
|
|
39797
|
-
if (isNodeOfType(unwrappedReturnedValue.callee, "Identifier")) {
|
|
39798
|
-
const setterSymbol = resolveConstIdentifierAlias(unwrappedReturnedValue.callee, scopes, true);
|
|
39799
|
-
if (setterToStateName.has(unwrappedReturnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
|
|
39800
|
-
}
|
|
39801
|
-
const invokedFunction = resolveSynchronouslyInvokedFunction(unwrappedReturnedValue.callee, scopes);
|
|
39802
|
-
if (invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes)) return false;
|
|
39803
|
-
if (invokedFunction) {
|
|
39804
|
-
const cleanupReturnProof = getResolvedFunctionCleanupReturnProof(invokedFunction, scopes);
|
|
39805
|
-
if (cleanupReturnProof.isValid) return cleanupReturnProof.hasCleanup;
|
|
39806
|
-
}
|
|
39807
|
-
if (!invokedFunction && isNodeOfType(unwrappedReturnedValue.callee, "Identifier") && isSetterIdentifier(unwrappedReturnedValue.callee.name)) return true;
|
|
39808
|
-
return isCleanupReturn(unwrappedReturnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
|
|
39809
|
-
}
|
|
39810
|
-
if (isNodeOfType(unwrappedReturnedValue, "Identifier")) return true;
|
|
38876
|
+
const STORAGE_GLOBAL_NAMES = new Set(["localStorage", "sessionStorage"]);
|
|
38877
|
+
const isBrowserStorageReceiver = (receiver) => {
|
|
38878
|
+
if (!receiver) return false;
|
|
38879
|
+
if (isNodeOfType(receiver, "Identifier")) return STORAGE_GLOBAL_NAMES.has(receiver.name);
|
|
38880
|
+
if (isNodeOfType(receiver, "MemberExpression")) return isNodeOfType(receiver.property, "Identifier") && STORAGE_GLOBAL_NAMES.has(receiver.property.name);
|
|
39811
38881
|
return false;
|
|
39812
38882
|
};
|
|
39813
|
-
const STORAGE_GLOBAL_NAMES = ["localStorage", "sessionStorage"];
|
|
39814
|
-
const isBrowserStorageReceiver = (receiver, scopes) => Boolean(receiver && STORAGE_GLOBAL_NAMES.some((storageName) => isProvenGlobalNamespaceReference(receiver, storageName, scopes)));
|
|
39815
38883
|
const STORAGE_HOOK_PATTERN = /^use\w*Storage/i;
|
|
39816
38884
|
const collectStorageHookSetterNames = (componentBody) => {
|
|
39817
38885
|
const setterNames = /* @__PURE__ */ new Set();
|
|
@@ -39843,16 +38911,23 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
|
|
|
39843
38911
|
});
|
|
39844
38912
|
return didFindOpaqueSetterCall;
|
|
39845
38913
|
};
|
|
39846
|
-
const
|
|
38914
|
+
const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
|
|
39847
38915
|
allowGlobalReactNamespace: true,
|
|
39848
38916
|
allowUnboundBareCalls: true,
|
|
39849
38917
|
resolveNamedAliases: true
|
|
39850
|
-
})
|
|
39851
|
-
const isReactRefCall = (expression, scopes) => isReactUseRefCall(expression, scopes) || isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "createRef", scopes, {
|
|
38918
|
+
}) || isReactApiCall(expression, "createRef", scopes, {
|
|
39852
38919
|
allowGlobalReactNamespace: true,
|
|
39853
38920
|
allowUnboundBareCalls: true,
|
|
39854
38921
|
resolveNamedAliases: true
|
|
39855
|
-
});
|
|
38922
|
+
}));
|
|
38923
|
+
const getDirectReactRefSymbol = (rawExpression, scopes) => {
|
|
38924
|
+
const expression = stripParenExpression(rawExpression);
|
|
38925
|
+
if (!isNodeOfType(expression, "Identifier")) return null;
|
|
38926
|
+
const symbol = scopes.symbolFor(expression);
|
|
38927
|
+
if (!symbol) return null;
|
|
38928
|
+
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
38929
|
+
return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
|
|
38930
|
+
};
|
|
39856
38931
|
const isReactNativeJsxElement = (openingElement, scopes) => {
|
|
39857
38932
|
if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
|
|
39858
38933
|
const symbol = scopes.symbolFor(openingElement.name);
|
|
@@ -39860,33 +38935,19 @@ const isReactNativeJsxElement = (openingElement, scopes) => {
|
|
|
39860
38935
|
return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
|
|
39861
38936
|
};
|
|
39862
38937
|
const isDirectHostJsxRef = (symbol, scopes) => {
|
|
39863
|
-
let
|
|
39864
|
-
const
|
|
39865
|
-
|
|
39866
|
-
|
|
39867
|
-
|
|
39868
|
-
if (!
|
|
39869
|
-
|
|
39870
|
-
|
|
39871
|
-
|
|
39872
|
-
|
|
39873
|
-
|
|
39874
|
-
|
|
39875
|
-
|
|
39876
|
-
const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
|
|
39877
|
-
if (!aliasSymbol || aliasInitializer !== expression) return false;
|
|
39878
|
-
pendingSymbols.push(aliasSymbol);
|
|
39879
|
-
continue;
|
|
39880
|
-
}
|
|
39881
|
-
if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
|
|
39882
|
-
const attribute = container.parent;
|
|
39883
|
-
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
39884
|
-
const openingElement = attribute.parent;
|
|
39885
|
-
if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
|
|
39886
|
-
didFindHostRef = true;
|
|
39887
|
-
}
|
|
39888
|
-
}
|
|
39889
|
-
return didFindHostRef;
|
|
38938
|
+
let hostRefCount = 0;
|
|
38939
|
+
for (const reference of symbol.references) {
|
|
38940
|
+
const expression = findTransparentExpressionRoot(reference.identifier);
|
|
38941
|
+
const container = expression.parent;
|
|
38942
|
+
if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
|
|
38943
|
+
if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
|
|
38944
|
+
const attribute = container.parent;
|
|
38945
|
+
if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
|
|
38946
|
+
const openingElement = attribute.parent;
|
|
38947
|
+
if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
|
|
38948
|
+
hostRefCount += 1;
|
|
38949
|
+
}
|
|
38950
|
+
return hostRefCount > 0;
|
|
39890
38951
|
};
|
|
39891
38952
|
const isIntrinsicRefCallbackParameter = (expression, scopes) => {
|
|
39892
38953
|
const identifier = stripParenExpression(expression);
|
|
@@ -39896,18 +38957,7 @@ const isIntrinsicRefCallbackParameter = (expression, scopes) => {
|
|
|
39896
38957
|
const rawFirstParameter = callback.params?.[0];
|
|
39897
38958
|
const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
|
|
39898
38959
|
const symbol = scopes.symbolFor(identifier);
|
|
39899
|
-
return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter
|
|
39900
|
-
if (reference.flag !== "read") return false;
|
|
39901
|
-
let referenceRoot = reference.identifier;
|
|
39902
|
-
while (referenceRoot.parent) {
|
|
39903
|
-
const parent = referenceRoot.parent;
|
|
39904
|
-
if (isNodeOfType(parent, "AssignmentExpression")) return parent.left !== referenceRoot;
|
|
39905
|
-
if (isNodeOfType(parent, "UpdateExpression")) return parent.argument !== referenceRoot;
|
|
39906
|
-
if (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) return parent.left !== referenceRoot;
|
|
39907
|
-
referenceRoot = parent;
|
|
39908
|
-
}
|
|
39909
|
-
return true;
|
|
39910
|
-
}));
|
|
38960
|
+
return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
|
|
39911
38961
|
};
|
|
39912
38962
|
const getDirectReactRefCall = (symbol, scopes) => {
|
|
39913
38963
|
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
@@ -39915,38 +38965,15 @@ const getDirectReactRefCall = (symbol, scopes) => {
|
|
|
39915
38965
|
const expression = stripParenExpression(initializer);
|
|
39916
38966
|
return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
|
|
39917
38967
|
};
|
|
39918
|
-
const isEmptyGlobalMapConstruction = (rawExpression, scopes) => {
|
|
39919
|
-
const expression = stripParenExpression(rawExpression);
|
|
39920
|
-
return isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Map" && scopes.isGlobalReference(expression.callee) && expression.arguments.length === 0;
|
|
39921
|
-
};
|
|
39922
|
-
const hasEmptyRefSentinelInitializer = (refCall, scopes) => {
|
|
39923
|
-
if (!isReactUseRefCall(refCall, scopes) || refCall.arguments.length > 1) return false;
|
|
39924
|
-
const [initialValue] = refCall.arguments;
|
|
39925
|
-
return !initialValue || isNodeOfType(initialValue, "Literal") && initialValue.value === null || isNodeOfType(initialValue, "Identifier") && initialValue.name === "undefined" && scopes.isGlobalReference(initialValue);
|
|
39926
|
-
};
|
|
39927
|
-
const isDirectLazyEmptyMapInitialization = (currentExpression, symbol, scopes) => {
|
|
39928
|
-
const assignment = currentExpression.parent;
|
|
39929
|
-
if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== currentExpression || assignment.operator !== "??=" || !isEmptyGlobalMapConstruction(assignment.right, scopes)) return false;
|
|
39930
|
-
const refOwner = findEnclosingFunction$1(symbol.bindingIdentifier);
|
|
39931
|
-
return refOwner !== null && findEnclosingFunction$1(assignment) === refOwner;
|
|
39932
|
-
};
|
|
39933
38968
|
const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
|
|
39934
|
-
const
|
|
39935
|
-
|
|
39936
|
-
const hasDirectEmptyMapInitializer = Boolean(initialValue && isEmptyGlobalMapConstruction(initialValue, scopes));
|
|
39937
|
-
const hasLazyEmptyMapInitializer = Boolean(refCall && hasEmptyRefSentinelInitializer(refCall, scopes));
|
|
39938
|
-
if (!hasDirectEmptyMapInitializer && !hasLazyEmptyMapInitializer) return false;
|
|
38969
|
+
const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
|
|
38970
|
+
if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
|
|
39939
38971
|
let intrinsicValueWriteCount = 0;
|
|
39940
|
-
let lazyEmptyMapInitializationCount = 0;
|
|
39941
38972
|
for (const reference of symbol.references) {
|
|
39942
38973
|
const identifier = findTransparentExpressionRoot(reference.identifier);
|
|
39943
38974
|
const currentMember = identifier.parent;
|
|
39944
38975
|
if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
|
|
39945
38976
|
const currentExpression = findTransparentExpressionRoot(currentMember);
|
|
39946
|
-
if (hasLazyEmptyMapInitializer && isDirectLazyEmptyMapInitialization(currentExpression, symbol, scopes)) {
|
|
39947
|
-
lazyEmptyMapInitializationCount += 1;
|
|
39948
|
-
continue;
|
|
39949
|
-
}
|
|
39950
38977
|
const methodMember = currentExpression.parent;
|
|
39951
38978
|
if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
|
|
39952
38979
|
const methodName = getStaticPropertyName(methodMember);
|
|
@@ -39959,7 +38986,7 @@ const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
|
|
|
39959
38986
|
if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
|
|
39960
38987
|
intrinsicValueWriteCount += 1;
|
|
39961
38988
|
}
|
|
39962
|
-
return intrinsicValueWriteCount > 0
|
|
38989
|
+
return intrinsicValueWriteCount > 0;
|
|
39963
38990
|
};
|
|
39964
38991
|
const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
39965
38992
|
const expression = stripParenExpression(rawExpression);
|
|
@@ -39973,11 +39000,7 @@ const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollecti
|
|
|
39973
39000
|
}
|
|
39974
39001
|
if (isNodeOfType(expression, "MemberExpression")) {
|
|
39975
39002
|
if (getStaticPropertyName(expression) === "current") {
|
|
39976
|
-
const symbol =
|
|
39977
|
-
allowUnboundBareCalls: true,
|
|
39978
|
-
includeCreateRef: true,
|
|
39979
|
-
resolveNamedAliases: true
|
|
39980
|
-
});
|
|
39003
|
+
const symbol = getDirectReactRefSymbol(expression.object, scopes);
|
|
39981
39004
|
return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
|
|
39982
39005
|
}
|
|
39983
39006
|
return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
|
|
@@ -39995,80 +39018,19 @@ const isCommittedDomSyncNode = (node, scopes) => {
|
|
|
39995
39018
|
if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
|
|
39996
39019
|
return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
|
|
39997
39020
|
};
|
|
39998
|
-
const
|
|
39999
|
-
|
|
40000
|
-
if (isNodeOfType(
|
|
40001
|
-
const receiverSymbol = scopes.symbolFor(expression);
|
|
40002
|
-
if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
|
|
40003
|
-
}
|
|
40004
|
-
if (hasProvenGlobalNamespaceReference(expression, EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES, scopes)) return true;
|
|
40005
|
-
const moduleBinding = resolveExternalModuleBinding(expression, scopes);
|
|
40006
|
-
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;
|
|
40007
|
-
if (isNodeOfType(expression, "CallExpression")) {
|
|
40008
|
-
const callee = stripParenExpression(expression.callee);
|
|
40009
|
-
return Boolean(isNodeOfType(callee, "MemberExpression") && getStaticPropertyName(callee) === "create" && isProvenHttpClientReceiver(callee.object, scopes, visitedSymbolIds));
|
|
40010
|
-
}
|
|
40011
|
-
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
40012
|
-
const symbol = scopes.symbolFor(expression);
|
|
40013
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
40014
|
-
if (resolveExternalModuleBinding(expression, scopes, /* @__PURE__ */ new Set(), true)) return false;
|
|
40015
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
40016
|
-
return Boolean(initializer && isProvenHttpClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
|
|
40017
|
-
};
|
|
40018
|
-
const isProvenTanStackQueryClientReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40019
|
-
const expression = stripParenExpression(rawExpression);
|
|
40020
|
-
if (isNodeOfType(expression, "Identifier")) {
|
|
40021
|
-
const receiverSymbol = scopes.symbolFor(expression);
|
|
40022
|
-
if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
|
|
40023
|
-
}
|
|
40024
|
-
if (isNodeOfType(expression, "CallExpression")) {
|
|
40025
|
-
const binding = resolveExternalModuleBinding(expression.callee, scopes);
|
|
40026
|
-
return Boolean(binding && binding.source === "@tanstack/react-query" && binding.exportedName === "useQueryClient");
|
|
40027
|
-
}
|
|
40028
|
-
if (isNodeOfType(expression, "NewExpression")) {
|
|
40029
|
-
const binding = resolveExternalModuleBinding(expression.callee, scopes);
|
|
40030
|
-
return Boolean(binding && TANSTACK_QUERY_CLIENT_MODULE_SOURCES.has(binding.source) && binding.exportedName === "QueryClient");
|
|
40031
|
-
}
|
|
40032
|
-
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
40033
|
-
const symbol = scopes.symbolFor(expression);
|
|
40034
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
40035
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
40036
|
-
return Boolean(initializer && isProvenTanStackQueryClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
|
|
40037
|
-
};
|
|
40038
|
-
const isProvenExternalResourceReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
40039
|
-
const expression = stripParenExpression(rawExpression);
|
|
40040
|
-
if (isDerivedFromProvenDomRefCurrent(expression, scopes) || isProvenBrowserApiReceiver(expression, "dom-event-target", scopes) || isProvenBrowserApiReceiver(expression, "xml-http-request", scopes) || isProvenHttpClientReceiver(expression, scopes)) return true;
|
|
40041
|
-
if (isNodeOfType(expression, "NewExpression")) return hasProvenGlobalNamespaceReference(expression.callee, EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES, scopes);
|
|
40042
|
-
if (!isNodeOfType(expression, "Identifier")) return false;
|
|
40043
|
-
const symbol = scopes.symbolFor(expression);
|
|
40044
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
|
|
40045
|
-
const initializer = getDirectUnreassignedInitializer(symbol);
|
|
40046
|
-
return Boolean(initializer && isProvenExternalResourceReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
|
|
40047
|
-
};
|
|
40048
|
-
const isProvenExternalSyncDirectCallee = (callee, scopes) => {
|
|
40049
|
-
if (hasProvenGlobalNamespaceReference(callee, EXTERNAL_SYNC_DIRECT_CALLEE_NAMES, scopes)) return true;
|
|
40050
|
-
const moduleBinding = resolveExternalModuleBinding(callee, scopes);
|
|
40051
|
-
return Boolean(moduleBinding && isKnownExternalSyncDirectModuleBinding(moduleBinding));
|
|
40052
|
-
};
|
|
40053
|
-
const isExternalSyncNode = (node, scopes) => {
|
|
40054
|
-
if (isNodeOfType(node, "NewExpression")) return hasProvenGlobalNamespaceReference(node.callee, EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS, scopes);
|
|
40055
|
-
if (isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression")) {
|
|
40056
|
-
const mutationTarget = isNodeOfType(node, "AssignmentExpression") ? node.left : node.argument;
|
|
40057
|
-
return isNodeOfType(mutationTarget, "MemberExpression") && getStaticPropertyName(mutationTarget) === "current" && Boolean(resolveReactRefSymbol(mutationTarget, scopes, {
|
|
40058
|
-
allowUnboundBareCalls: true,
|
|
40059
|
-
includeCreateRef: true,
|
|
40060
|
-
resolveNamedAliases: true
|
|
40061
|
-
}));
|
|
40062
|
-
}
|
|
39021
|
+
const isExternalSyncNode = (node) => {
|
|
39022
|
+
if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
|
|
39023
|
+
if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
|
|
40063
39024
|
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
40064
|
-
if (
|
|
39025
|
+
if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
|
|
40065
39026
|
if (!isNodeOfType(node.callee, "MemberExpression")) return false;
|
|
40066
39027
|
const propertyName = getStaticPropertyName(node.callee);
|
|
40067
39028
|
if (propertyName === null) return false;
|
|
40068
|
-
if (
|
|
40069
|
-
if (
|
|
40070
|
-
if (
|
|
40071
|
-
|
|
39029
|
+
if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
|
|
39030
|
+
if (isBrowserStorageReceiver(node.callee.object)) return true;
|
|
39031
|
+
if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
|
|
39032
|
+
const receiverRootName = getRootIdentifierName(node.callee.object);
|
|
39033
|
+
return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
|
|
40072
39034
|
};
|
|
40073
39035
|
const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, scopes, allowCommittedDomSync) => {
|
|
40074
39036
|
if (!isFunctionLike$1(effectCallback)) return false;
|
|
@@ -40077,7 +39039,7 @@ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateNa
|
|
|
40077
39039
|
} else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, setterSymbolIdToStateName, scopes, true)) return true;
|
|
40078
39040
|
let didFindExternalCall = false;
|
|
40079
39041
|
visitSynchronousFunctionBodies(analysisFunctions, (child) => {
|
|
40080
|
-
if (isExternalSyncNode(child
|
|
39042
|
+
if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
|
|
40081
39043
|
});
|
|
40082
39044
|
return didFindExternalCall;
|
|
40083
39045
|
};
|
|
@@ -40099,11 +39061,11 @@ const noEffectChain = defineRule({
|
|
|
40099
39061
|
setterToStateName.set(binding.setterName, binding.valueName);
|
|
40100
39062
|
if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
|
|
40101
39063
|
const stateIdentifier = binding.declarator.id.elements[0];
|
|
40102
|
-
const setterIdentifier = binding.declarator.id.elements[1];
|
|
40103
39064
|
if (isNodeOfType(stateIdentifier, "Identifier")) {
|
|
40104
39065
|
const stateSymbol = context.scopes.symbolFor(stateIdentifier);
|
|
40105
39066
|
if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
|
|
40106
39067
|
}
|
|
39068
|
+
const setterIdentifier = binding.declarator.id.elements[1];
|
|
40107
39069
|
if (isNodeOfType(setterIdentifier, "Identifier")) {
|
|
40108
39070
|
const setterSymbol = context.scopes.symbolFor(setterIdentifier);
|
|
40109
39071
|
if (setterSymbol) setterSymbolIdToStateName.set(setterSymbol.id, binding.valueName);
|
|
@@ -40116,11 +39078,10 @@ const noEffectChain = defineRule({
|
|
|
40116
39078
|
const callback = getEffectCallback(effectCall, context.scopes);
|
|
40117
39079
|
if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
|
|
40118
39080
|
const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
|
|
40119
|
-
const stateWrites = collectStateWritesInEffect(
|
|
39081
|
+
const stateWrites = collectStateWritesInEffect(analysisFunctions, setterSymbolIdToStateName, context.scopes);
|
|
40120
39082
|
const writtenStateNames = new Set(stateWrites.keys());
|
|
40121
39083
|
effectInfos.push({
|
|
40122
39084
|
node: effectCall,
|
|
40123
|
-
callback,
|
|
40124
39085
|
dependencyStateSymbolIds: collectDependencyStateSymbolIds(effectCall, stateSymbolIdSet, context.scopes),
|
|
40125
39086
|
stateWrites,
|
|
40126
39087
|
analysisFunctions,
|
|
@@ -40137,10 +39098,10 @@ const noEffectChain = defineRule({
|
|
|
40137
39098
|
if (readerEffect.isExternalSync) continue;
|
|
40138
39099
|
if (readerEffect.dependencyStateSymbolIds.size === 0) continue;
|
|
40139
39100
|
let chainedStateName = null;
|
|
40140
|
-
for (const writtenName of writerEffect.stateWrites
|
|
39101
|
+
for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
|
|
40141
39102
|
const writtenStateSymbolId = stateSymbolIds.get(writtenName);
|
|
40142
39103
|
if (writtenStateSymbolId === void 0 || !readerEffect.dependencyStateSymbolIds.has(writtenStateSymbolId)) continue;
|
|
40143
|
-
if (!canStateWriteReachReaderWork(
|
|
39104
|
+
if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
|
|
40144
39105
|
chainedStateName = writtenName;
|
|
40145
39106
|
break;
|
|
40146
39107
|
}
|
|
@@ -40529,7 +39490,7 @@ const noEffectEventInDeps = defineRule({
|
|
|
40529
39490
|
title: "Effect Event listed in effect deps",
|
|
40530
39491
|
tags: ["test-noise"],
|
|
40531
39492
|
severity: "error",
|
|
40532
|
-
recommendation: "Call the useEffectEvent function inside the effect body and don't list it in the deps. It
|
|
39493
|
+
recommendation: "Call the useEffectEvent function inside the effect body and don't list it in the deps. It changes on every render on purpose.",
|
|
40533
39494
|
create: (context) => {
|
|
40534
39495
|
const componentBindings = createComponentBindingStackTracker({ onVariableDeclarator: (declaratorNode) => {
|
|
40535
39496
|
if (!isNodeOfType(declaratorNode, "VariableDeclarator")) return;
|
|
@@ -40551,7 +39512,7 @@ const noEffectEventInDeps = defineRule({
|
|
|
40551
39512
|
if (!isNodeOfType(element, "Identifier")) continue;
|
|
40552
39513
|
if (componentBindings.isBoundName(element.name)) context.report({
|
|
40553
39514
|
node: element,
|
|
40554
|
-
message: `Listing "${element.name}" in the deps
|
|
39515
|
+
message: `Listing "${element.name}" in the deps re-runs your effect every render & defeats useEffectEvent.`
|
|
40555
39516
|
});
|
|
40556
39517
|
}
|
|
40557
39518
|
}
|
|
@@ -40594,7 +39555,6 @@ const noEffectWithFreshDeps = defineRule({
|
|
|
40594
39555
|
title: "Effect dependency recreated every render",
|
|
40595
39556
|
severity: "error",
|
|
40596
39557
|
category: "State & Effects",
|
|
40597
|
-
disabledWhen: ["react-compiler"],
|
|
40598
39558
|
recommendation: "Move the value inside the hook body and depend on its simple inputs instead, or wrap it in useMemo / useCallback so it stays the same between renders.",
|
|
40599
39559
|
create: (context) => ({ CallExpression(node) {
|
|
40600
39560
|
for (const finding of findForwardedFreshHookDependencies(node, context, EFFECT_HOOK_NAMES$1)) context.report({
|
|
@@ -43535,13 +42495,14 @@ const noInlineExhaustiveStyle = defineRule({
|
|
|
43535
42495
|
});
|
|
43536
42496
|
//#endregion
|
|
43537
42497
|
//#region src/plugin/rules/performance/no-inline-prop-on-memo-component.ts
|
|
43538
|
-
const
|
|
43539
|
-
const isMemoCall = (node) => isNodeOfType(node, "CallExpression") && MEMO_CALLEE_NAMES.has(flattenCalleeName(node.callee) ?? "");
|
|
43540
|
-
const hasCustomComparator = (node, scopes) => {
|
|
42498
|
+
const isMemoCall = (node) => {
|
|
43541
42499
|
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
43542
|
-
|
|
43543
|
-
|
|
42500
|
+
if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "memo") return true;
|
|
42501
|
+
if (isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.object, "Identifier") && node.callee.object.name === "React" && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === "memo") return true;
|
|
42502
|
+
return false;
|
|
43544
42503
|
};
|
|
42504
|
+
const isDefaultEquivalentComparator = (comparator) => isNodeOfType(comparator, "Identifier") && (comparator.name === "undefined" || comparator.name === "shallowEqual");
|
|
42505
|
+
const hasCustomComparator = (node) => isNodeOfType(node, "CallExpression") && (node.arguments?.length ?? 0) >= 2 && !isDefaultEquivalentComparator(node.arguments?.[1]);
|
|
43545
42506
|
const isInlineReference = (node, scopes) => {
|
|
43546
42507
|
const referenceNode = unwrapObjectIntegrityExpression(node, scopes);
|
|
43547
42508
|
if (isNodeOfType(referenceNode, "ArrowFunctionExpression") || isNodeOfType(referenceNode, "FunctionExpression") || isNodeOfType(referenceNode, "CallExpression") && isNodeOfType(referenceNode.callee, "MemberExpression") && isNodeOfType(referenceNode.callee.property, "Identifier") && referenceNode.callee.property.name === "bind") return "functions";
|
|
@@ -43555,17 +42516,16 @@ const noInlinePropOnMemoComponent = defineRule({
|
|
|
43555
42516
|
title: "Inline prop defeats memo()",
|
|
43556
42517
|
tags: ["test-noise"],
|
|
43557
42518
|
severity: "warn",
|
|
43558
|
-
disabledWhen: ["react-compiler"],
|
|
43559
42519
|
recommendation: "Move the inline `() => ...` / `[]` / `{}` to a stable value with useMemo, useCallback, or module scope, so the memoized child stops redrawing on every parent render",
|
|
43560
42520
|
create: (context) => {
|
|
43561
42521
|
const memoizedComponentNames = /* @__PURE__ */ new Set();
|
|
43562
42522
|
return {
|
|
43563
42523
|
VariableDeclarator(node) {
|
|
43564
42524
|
if (!isNodeOfType(node.id, "Identifier") || !node.init) return;
|
|
43565
|
-
if (isMemoCall(node.init) && !hasCustomComparator(node.init
|
|
42525
|
+
if (isMemoCall(node.init) && !hasCustomComparator(node.init)) memoizedComponentNames.add(node.id.name);
|
|
43566
42526
|
},
|
|
43567
42527
|
ExportDefaultDeclaration(node) {
|
|
43568
|
-
if (node.declaration && isNodeOfType(node.declaration, "CallExpression") && isMemoCall(node.declaration) && !hasCustomComparator(node.declaration
|
|
42528
|
+
if (node.declaration && isNodeOfType(node.declaration, "CallExpression") && isMemoCall(node.declaration) && !hasCustomComparator(node.declaration)) {
|
|
43569
42529
|
const innerArgument = node.declaration.arguments?.[0];
|
|
43570
42530
|
if (isNodeOfType(innerArgument, "Identifier")) memoizedComponentNames.add(innerArgument.name);
|
|
43571
42531
|
}
|
|
@@ -44925,6 +43885,16 @@ const noManyBooleanProps = defineRule({
|
|
|
44925
43885
|
}
|
|
44926
43886
|
});
|
|
44927
43887
|
//#endregion
|
|
43888
|
+
//#region src/plugin/utils/is-global-match-media-call.ts
|
|
43889
|
+
const isGlobalMatchMediaCall = (node, scopes) => {
|
|
43890
|
+
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
43891
|
+
const callee = stripParenExpression(node.callee);
|
|
43892
|
+
if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
|
|
43893
|
+
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
|
|
43894
|
+
const receiver = stripParenExpression(callee.object);
|
|
43895
|
+
return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
|
|
43896
|
+
};
|
|
43897
|
+
//#endregion
|
|
44928
43898
|
//#region src/plugin/rules/performance/no-match-media-in-state-initializer.ts
|
|
44929
43899
|
const REACT_USE_STATE_OPTIONS = {
|
|
44930
43900
|
allowGlobalReactNamespace: true,
|
|
@@ -56410,7 +55380,6 @@ const preferModuleScopePureFunction = defineRule({
|
|
|
56410
55380
|
tags: ["test-noise"],
|
|
56411
55381
|
severity: "warn",
|
|
56412
55382
|
category: "Architecture",
|
|
56413
|
-
disabledWhen: ["react-compiler"],
|
|
56414
55383
|
recommendation: "Move the function above the component, at the top of the file. It doesn't use local state, so rebuilding it each update is wasted work.",
|
|
56415
55384
|
create: (context) => {
|
|
56416
55385
|
const report = (functionNode, name, componentName) => {
|
|
@@ -56450,6 +55419,17 @@ const preferModuleScopePureFunction = defineRule({
|
|
|
56450
55419
|
}
|
|
56451
55420
|
});
|
|
56452
55421
|
//#endregion
|
|
55422
|
+
//#region src/plugin/utils/get-require-call-source.ts
|
|
55423
|
+
const getRequireCallSource = (expression) => {
|
|
55424
|
+
const unwrappedExpression = stripParenExpression(expression);
|
|
55425
|
+
if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
|
|
55426
|
+
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
|
|
55427
|
+
if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
|
|
55428
|
+
const [firstArgument] = unwrappedExpression.arguments ?? [];
|
|
55429
|
+
if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
|
|
55430
|
+
return typeof firstArgument.value === "string" ? firstArgument.value : null;
|
|
55431
|
+
};
|
|
55432
|
+
//#endregion
|
|
56453
55433
|
//#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
|
|
56454
55434
|
const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
|
|
56455
55435
|
const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
|
|
@@ -57041,10 +56021,10 @@ const classifyCallableReadsInsideEffect = (callableIdentifier, effectCallback, c
|
|
|
57041
56021
|
const preferUseEffectEvent = defineRule({
|
|
57042
56022
|
id: "prefer-use-effect-event",
|
|
57043
56023
|
title: "Effect re-subscribes on a changing callback",
|
|
57044
|
-
requires: ["react:19
|
|
56024
|
+
requires: ["react:19"],
|
|
57045
56025
|
tags: ["test-noise"],
|
|
57046
56026
|
severity: "warn",
|
|
57047
|
-
recommendation: "Wrap the callback with `useEffectEvent(callback)` (React 19
|
|
56027
|
+
recommendation: "Wrap the callback with `useEffectEvent(callback)` (React 19+) and call it inside the sub-handler. An Effect Event always sees the latest props and state but isn't a dependency, so the effect won't re-subscribe every time the parent redraws. See https://react.dev/reference/react/useEffectEvent",
|
|
57048
56028
|
create: (context) => {
|
|
57049
56029
|
const checkComponent = (componentBody) => {
|
|
57050
56030
|
if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
|
|
@@ -58649,6 +57629,7 @@ const reduxUseselectorInlineDerivation = defineRule({
|
|
|
58649
57629
|
title: "useSelector derives data inline",
|
|
58650
57630
|
severity: "warn",
|
|
58651
57631
|
category: "Performance",
|
|
57632
|
+
disabledWhen: ["react-compiler"],
|
|
58652
57633
|
recommendation: "Select the raw slice and memoize derivation so Redux actions do not rebuild a collection and redraw this component.",
|
|
58653
57634
|
create: (context) => {
|
|
58654
57635
|
let aliases = /* @__PURE__ */ new Set();
|
|
@@ -58708,6 +57689,7 @@ const reduxUseselectorReturnsNewCollection = defineRule({
|
|
|
58708
57689
|
title: "useSelector returns a new collection",
|
|
58709
57690
|
severity: "warn",
|
|
58710
57691
|
category: "Performance",
|
|
57692
|
+
disabledWhen: ["react-compiler"],
|
|
58711
57693
|
recommendation: "Return a stable selected value, split selectors, or pass `shallowEqual` so every Redux action does not redraw this component.",
|
|
58712
57694
|
create: (context) => {
|
|
58713
57695
|
let aliases = /* @__PURE__ */ new Set();
|
|
@@ -58833,7 +57815,6 @@ const renderingHoistJsx = defineRule({
|
|
|
58833
57815
|
title: "Constant JSX rebuilt each render",
|
|
58834
57816
|
tags: ["test-noise"],
|
|
58835
57817
|
severity: "warn",
|
|
58836
|
-
disabledWhen: ["react-compiler"],
|
|
58837
57818
|
recommendation: "Move the static JSX out to the top of the file: `const ICON = <svg>...</svg>`, so it isn't rebuilt on every render",
|
|
58838
57819
|
create: (context) => {
|
|
58839
57820
|
let componentDepth = 0;
|
|
@@ -59280,9 +58261,8 @@ const renderingHydrationNoFlicker = defineRule({
|
|
|
59280
58261
|
id: "rendering-hydration-no-flicker",
|
|
59281
58262
|
title: "useEffect setState flashes on mount",
|
|
59282
58263
|
tags: ["test-noise"],
|
|
59283
|
-
requires: ["ssr"],
|
|
59284
58264
|
severity: "warn",
|
|
59285
|
-
recommendation: "
|
|
58265
|
+
recommendation: "Use `useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)` or add `suppressHydrationWarning` to the element",
|
|
59286
58266
|
create: (context) => ({ CallExpression(node) {
|
|
59287
58267
|
if (!isHookCall$2(node, USE_EFFECT_ONLY) || (node.arguments?.length ?? 0) < 2) return;
|
|
59288
58268
|
const depsNode = node.arguments[1];
|
|
@@ -59735,7 +58715,7 @@ const renderingUsetransitionLoading = defineRule({
|
|
|
59735
58715
|
title: "Loading useState forces extra render",
|
|
59736
58716
|
tags: ["test-noise"],
|
|
59737
58717
|
severity: "warn",
|
|
59738
|
-
recommendation: "Replace with `const [isPending, startTransition] = useTransition()`, which
|
|
58718
|
+
recommendation: "Replace with `const [isPending, startTransition] = useTransition()`, which skips the extra render for the loading flag",
|
|
59739
58719
|
create: (context) => ({ VariableDeclarator(node) {
|
|
59740
58720
|
if (!isNodeOfType(node.id, "ArrayPattern") || !node.id.elements?.length) return;
|
|
59741
58721
|
if (!node.init || !isHookCall$2(node.init, "useState")) return;
|
|
@@ -59767,7 +58747,7 @@ const renderingUsetransitionLoading = defineRule({
|
|
|
59767
58747
|
}
|
|
59768
58748
|
context.report({
|
|
59769
58749
|
node: node.init,
|
|
59770
|
-
message: `This
|
|
58750
|
+
message: `This adds an extra render because useState for "${stateVariableName}" re-renders just for the loading flag, so if it's a state change & not a data fetch, use useTransition instead`
|
|
59771
58751
|
});
|
|
59772
58752
|
} })
|
|
59773
58753
|
});
|
|
@@ -60082,7 +59062,6 @@ const rerenderDependencies = defineRule({
|
|
|
60082
59062
|
title: "Unstable value recreated every render",
|
|
60083
59063
|
tags: ["test-noise"],
|
|
60084
59064
|
severity: "error",
|
|
60085
|
-
disabledWhen: ["react-compiler"],
|
|
60086
59065
|
recommendation: "Move it into a useMemo, useRef, or a constant outside the component so it stays the same between renders.",
|
|
60087
59066
|
create: (context) => ({ CallExpression(node) {
|
|
60088
59067
|
if (!isReactHookCall(node, HOOKS_WITH_DEPS, context.scopes) || node.arguments.length < 2) return;
|
|
@@ -60495,7 +59474,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
|
|
|
60495
59474
|
title: "useMemo before an early return",
|
|
60496
59475
|
tags: ["test-noise"],
|
|
60497
59476
|
severity: "warn",
|
|
60498
|
-
recommendation: "Move the JSX into a child component
|
|
59477
|
+
recommendation: "Move the JSX into a child component wrapped in memo, so the parent's early return skips it",
|
|
60499
59478
|
create: (context) => {
|
|
60500
59479
|
const inspectFunctionBody = (statements) => {
|
|
60501
59480
|
let memoNode = null;
|
|
@@ -60521,7 +59500,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
|
|
|
60521
59500
|
if (callbackGuardTests.some((guardTest) => areConditionsStructurallyEqual(stmt.test, guardTest))) continue;
|
|
60522
59501
|
context.report({
|
|
60523
59502
|
node: memoNode,
|
|
60524
|
-
message: "This
|
|
59503
|
+
message: "This runs even when the component bails out because the useMemo builds JSX before an early return, so move the JSX into a child wrapped in memo to skip it on the early return"
|
|
60525
59504
|
});
|
|
60526
59505
|
return;
|
|
60527
59506
|
}
|
|
@@ -61024,7 +60003,6 @@ const rerenderMemoWithDefaultValue = defineRule({
|
|
|
61024
60003
|
title: "Empty default prop breaks memo",
|
|
61025
60004
|
tags: ["test-noise"],
|
|
61026
60005
|
severity: "warn",
|
|
61027
|
-
disabledWhen: ["react-compiler"],
|
|
61028
60006
|
recommendation: "Move it to the top of the file: `const EMPTY_ITEMS: Item[] = []`, then use that as the default value",
|
|
61029
60007
|
create: (context) => {
|
|
61030
60008
|
let memoRegistry = /* @__PURE__ */ new Map();
|
|
@@ -61850,7 +60828,6 @@ const rnListDataMapped = defineRule({
|
|
|
61850
60828
|
title: "List data rebuilt every render",
|
|
61851
60829
|
tags: ["test-noise"],
|
|
61852
60830
|
requires: ["react-native"],
|
|
61853
|
-
disabledWhen: ["react-compiler"],
|
|
61854
60831
|
severity: "warn",
|
|
61855
60832
|
recommendation: "This builds a new array each time the parent redraws, so every row redraws too. Wrap it in `useMemo(() => items.map(...), [items])` to keep the same array.",
|
|
61856
60833
|
create: (context) => ({ JSXOpeningElement(node) {
|
|
@@ -64204,66 +63181,6 @@ const roleHasRequiredAriaProps = defineRule({
|
|
|
64204
63181
|
} })
|
|
64205
63182
|
});
|
|
64206
63183
|
//#endregion
|
|
64207
|
-
//#region src/plugin/constants/global-aria-properties.ts
|
|
64208
|
-
const GLOBAL_ARIA_PROPERTIES = new Set([
|
|
64209
|
-
"aria-atomic",
|
|
64210
|
-
"aria-braillelabel",
|
|
64211
|
-
"aria-brailleroledescription",
|
|
64212
|
-
"aria-busy",
|
|
64213
|
-
"aria-controls",
|
|
64214
|
-
"aria-current",
|
|
64215
|
-
"aria-describedby",
|
|
64216
|
-
"aria-description",
|
|
64217
|
-
"aria-details",
|
|
64218
|
-
"aria-disabled",
|
|
64219
|
-
"aria-dropeffect",
|
|
64220
|
-
"aria-errormessage",
|
|
64221
|
-
"aria-flowto",
|
|
64222
|
-
"aria-grabbed",
|
|
64223
|
-
"aria-haspopup",
|
|
64224
|
-
"aria-hidden",
|
|
64225
|
-
"aria-invalid",
|
|
64226
|
-
"aria-keyshortcuts",
|
|
64227
|
-
"aria-label",
|
|
64228
|
-
"aria-labelledby",
|
|
64229
|
-
"aria-live",
|
|
64230
|
-
"aria-owns",
|
|
64231
|
-
"aria-relevant",
|
|
64232
|
-
"aria-roledescription"
|
|
64233
|
-
]);
|
|
64234
|
-
//#endregion
|
|
64235
|
-
//#region src/plugin/constants/prohibited-aria-properties-by-role.ts
|
|
64236
|
-
const ACCESSIBLE_NAME_PROPERTIES = new Set([
|
|
64237
|
-
"aria-braillelabel",
|
|
64238
|
-
"aria-label",
|
|
64239
|
-
"aria-labelledby"
|
|
64240
|
-
]);
|
|
64241
|
-
const PROHIBITED_ARIA_PROPERTIES_BY_ROLE = {
|
|
64242
|
-
caption: ACCESSIBLE_NAME_PROPERTIES,
|
|
64243
|
-
code: ACCESSIBLE_NAME_PROPERTIES,
|
|
64244
|
-
definition: ACCESSIBLE_NAME_PROPERTIES,
|
|
64245
|
-
deletion: ACCESSIBLE_NAME_PROPERTIES,
|
|
64246
|
-
emphasis: ACCESSIBLE_NAME_PROPERTIES,
|
|
64247
|
-
generic: new Set([
|
|
64248
|
-
"aria-braillelabel",
|
|
64249
|
-
"aria-brailleroledescription",
|
|
64250
|
-
"aria-label",
|
|
64251
|
-
"aria-labelledby",
|
|
64252
|
-
"aria-roledescription"
|
|
64253
|
-
]),
|
|
64254
|
-
insertion: ACCESSIBLE_NAME_PROPERTIES,
|
|
64255
|
-
mark: ACCESSIBLE_NAME_PROPERTIES,
|
|
64256
|
-
none: ACCESSIBLE_NAME_PROPERTIES,
|
|
64257
|
-
paragraph: ACCESSIBLE_NAME_PROPERTIES,
|
|
64258
|
-
presentation: ACCESSIBLE_NAME_PROPERTIES,
|
|
64259
|
-
strong: ACCESSIBLE_NAME_PROPERTIES,
|
|
64260
|
-
subscript: ACCESSIBLE_NAME_PROPERTIES,
|
|
64261
|
-
superscript: ACCESSIBLE_NAME_PROPERTIES,
|
|
64262
|
-
term: ACCESSIBLE_NAME_PROPERTIES,
|
|
64263
|
-
time: ACCESSIBLE_NAME_PROPERTIES,
|
|
64264
|
-
tooltip: ACCESSIBLE_NAME_PROPERTIES
|
|
64265
|
-
};
|
|
64266
|
-
//#endregion
|
|
64267
63184
|
//#region src/plugin/constants/role-supports-aria-props.ts
|
|
64268
63185
|
const ROLE_SUPPORTS_ARIA_PROPS = {
|
|
64269
63186
|
alert: new Set([
|
|
@@ -67364,12 +66281,6 @@ const ROLE_SUPPORTS_ARIA_PROPS = {
|
|
|
67364
66281
|
])
|
|
67365
66282
|
};
|
|
67366
66283
|
//#endregion
|
|
67367
|
-
//#region src/plugin/utils/is-aria-property-supported-by-role.ts
|
|
67368
|
-
const isAriaPropertySupportedByRole = (role, property) => {
|
|
67369
|
-
if (PROHIBITED_ARIA_PROPERTIES_BY_ROLE[role]?.has(property)) return false;
|
|
67370
|
-
return ROLE_SUPPORTS_ARIA_PROPS[role]?.has(property) === true || GLOBAL_ARIA_PROPERTIES.has(property);
|
|
67371
|
-
};
|
|
67372
|
-
//#endregion
|
|
67373
66284
|
//#region src/plugin/rules/a11y/role-supports-aria-props.ts
|
|
67374
66285
|
const buildMessageDefault = (roles, propName) => {
|
|
67375
66286
|
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.`;
|
|
@@ -67406,10 +66317,16 @@ const roleSupportsAriaProps = defineRule({
|
|
|
67406
66317
|
const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
|
|
67407
66318
|
const roleCandidates = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [getImplicitRole(node, elementType, context.scopes)].filter((role) => role !== null);
|
|
67408
66319
|
if (roleCandidates === null || roleCandidates.length === 0) return;
|
|
67409
|
-
|
|
66320
|
+
const supportedSets = [];
|
|
66321
|
+
for (const role of roleCandidates) {
|
|
66322
|
+
if (!VALID_ARIA_ROLES.has(role)) return;
|
|
66323
|
+
const supported = ROLE_SUPPORTS_ARIA_PROPS[role];
|
|
66324
|
+
if (!supported) return;
|
|
66325
|
+
supportedSets.push(supported);
|
|
66326
|
+
}
|
|
67410
66327
|
const isImplicit = !roleAttribute;
|
|
67411
66328
|
for (const { attribute, propName } of ariaAttributes) {
|
|
67412
|
-
if (
|
|
66329
|
+
if (supportedSets.some((supported) => supported.has(propName))) continue;
|
|
67413
66330
|
context.report({
|
|
67414
66331
|
node: attribute,
|
|
67415
66332
|
message: isImplicit ? buildMessageImplicit(roleCandidates[0], propName, elementType) : buildMessageDefault(roleCandidates, propName)
|
|
@@ -76191,7 +75108,7 @@ const EXTERNAL_RULES = [
|
|
|
76191
75108
|
{
|
|
76192
75109
|
key: "react-hooks-js/set-state-in-effect",
|
|
76193
75110
|
source: "react-compiler",
|
|
76194
|
-
severity: "
|
|
75111
|
+
severity: "error"
|
|
76195
75112
|
},
|
|
76196
75113
|
{
|
|
76197
75114
|
key: "react-hooks-js/globals",
|