oxlint-plugin-react-doctor 0.7.5-dev.654849 → 0.7.5-dev.70eff9a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +331 -96
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5934,6 +5934,21 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
|
|
|
5934
5934
|
}
|
|
5935
5935
|
});
|
|
5936
5936
|
//#endregion
|
|
5937
|
+
//#region src/plugin/utils/get-static-property-key-name.ts
|
|
5938
|
+
const getStaticPropertyKeyName = (node, options = {}) => {
|
|
5939
|
+
if (!isNodeOfType(node, "Property")) return null;
|
|
5940
|
+
if (node.computed) {
|
|
5941
|
+
if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
|
|
5942
|
+
return null;
|
|
5943
|
+
}
|
|
5944
|
+
if (isNodeOfType(node.key, "Identifier")) return node.key.name;
|
|
5945
|
+
if (isNodeOfType(node.key, "Literal")) {
|
|
5946
|
+
if (typeof node.key.value === "string") return node.key.value;
|
|
5947
|
+
if (options.stringifyNonStringLiterals) return String(node.key.value);
|
|
5948
|
+
}
|
|
5949
|
+
return null;
|
|
5950
|
+
};
|
|
5951
|
+
//#endregion
|
|
5937
5952
|
//#region src/plugin/utils/is-presentation-role.ts
|
|
5938
5953
|
const isPresentationRole = (openingElement) => {
|
|
5939
5954
|
const roleAttribute = hasJsxPropIgnoreCase(openingElement.attributes, "role");
|
|
@@ -5978,6 +5993,21 @@ const isPureEventBlockerHandler = (attribute) => {
|
|
|
5978
5993
|
return false;
|
|
5979
5994
|
};
|
|
5980
5995
|
//#endregion
|
|
5996
|
+
//#region src/plugin/utils/resolve-const-identifier-alias.ts
|
|
5997
|
+
const resolveConstIdentifierAlias = (identifier, scopes) => {
|
|
5998
|
+
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
5999
|
+
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
6000
|
+
let symbol = scopes.symbolFor(identifier);
|
|
6001
|
+
while (symbol?.kind === "const") {
|
|
6002
|
+
if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
6003
|
+
visitedSymbolIds.add(symbol.id);
|
|
6004
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
6005
|
+
if (!isNodeOfType(initializer, "Identifier")) return symbol;
|
|
6006
|
+
symbol = scopes.symbolFor(initializer);
|
|
6007
|
+
}
|
|
6008
|
+
return symbol;
|
|
6009
|
+
};
|
|
6010
|
+
//#endregion
|
|
5981
6011
|
//#region src/plugin/rules/a11y/click-events-have-key-events.ts
|
|
5982
6012
|
const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
|
|
5983
6013
|
const KEY_HANDLERS = [
|
|
@@ -5988,6 +6018,63 @@ const KEY_HANDLERS = [
|
|
|
5988
6018
|
"onKeyDownCapture",
|
|
5989
6019
|
"onKeyPressCapture"
|
|
5990
6020
|
];
|
|
6021
|
+
const CLICK_HANDLERS = ["onClick", "onClickCapture"];
|
|
6022
|
+
const TRANSPARENT_SPREAD_EVENT_NAMES = new Set([...CLICK_HANDLERS, ...KEY_HANDLERS].map((eventName) => eventName.toLowerCase()));
|
|
6023
|
+
const CONSERVATIVE_SPREAD_PROP_NAMES = new Set([
|
|
6024
|
+
"aria-hidden",
|
|
6025
|
+
"onmouseenter",
|
|
6026
|
+
"onmouseover",
|
|
6027
|
+
"role"
|
|
6028
|
+
]);
|
|
6029
|
+
const resolveSpreadObjectExpression = (expression, scopes) => {
|
|
6030
|
+
const innerExpression = stripParenExpression(expression);
|
|
6031
|
+
if (isNodeOfType(innerExpression, "ObjectExpression")) return innerExpression;
|
|
6032
|
+
if (!isNodeOfType(innerExpression, "Identifier")) return null;
|
|
6033
|
+
const symbol = resolveConstIdentifierAlias(innerExpression, scopes);
|
|
6034
|
+
if (symbol?.kind !== "const" || !symbol.initializer) return null;
|
|
6035
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
6036
|
+
return isNodeOfType(initializer, "ObjectExpression") ? initializer : null;
|
|
6037
|
+
};
|
|
6038
|
+
const collectTransparentSpreadEventNames = (expression, scopes, eventValues, visitedObjectExpressions) => {
|
|
6039
|
+
const objectExpression = resolveSpreadObjectExpression(expression, scopes);
|
|
6040
|
+
if (!objectExpression || visitedObjectExpressions.has(objectExpression)) return false;
|
|
6041
|
+
visitedObjectExpressions.add(objectExpression);
|
|
6042
|
+
let isTransparent = true;
|
|
6043
|
+
for (const property of objectExpression.properties) {
|
|
6044
|
+
if (isNodeOfType(property, "SpreadElement")) {
|
|
6045
|
+
if (!collectTransparentSpreadEventNames(property.argument, scopes, eventValues, visitedObjectExpressions)) {
|
|
6046
|
+
isTransparent = false;
|
|
6047
|
+
break;
|
|
6048
|
+
}
|
|
6049
|
+
continue;
|
|
6050
|
+
}
|
|
6051
|
+
if (!isNodeOfType(property, "Property")) {
|
|
6052
|
+
isTransparent = false;
|
|
6053
|
+
break;
|
|
6054
|
+
}
|
|
6055
|
+
const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
6056
|
+
if (!propertyName) {
|
|
6057
|
+
isTransparent = false;
|
|
6058
|
+
break;
|
|
6059
|
+
}
|
|
6060
|
+
const normalizedPropertyName = propertyName.toLowerCase();
|
|
6061
|
+
if (CONSERVATIVE_SPREAD_PROP_NAMES.has(normalizedPropertyName)) {
|
|
6062
|
+
isTransparent = false;
|
|
6063
|
+
break;
|
|
6064
|
+
}
|
|
6065
|
+
if (TRANSPARENT_SPREAD_EVENT_NAMES.has(normalizedPropertyName)) eventValues.set(normalizedPropertyName, property.value);
|
|
6066
|
+
}
|
|
6067
|
+
visitedObjectExpressions.delete(objectExpression);
|
|
6068
|
+
return isTransparent;
|
|
6069
|
+
};
|
|
6070
|
+
const getTransparentSpreadEventValues = (attributes, scopes) => {
|
|
6071
|
+
const eventValues = /* @__PURE__ */ new Map();
|
|
6072
|
+
for (const attribute of attributes) {
|
|
6073
|
+
if (!isNodeOfType(attribute, "JSXSpreadAttribute")) continue;
|
|
6074
|
+
if (!collectTransparentSpreadEventNames(attribute.argument, scopes, eventValues, /* @__PURE__ */ new Set())) return null;
|
|
6075
|
+
}
|
|
6076
|
+
return eventValues;
|
|
6077
|
+
};
|
|
5991
6078
|
const FOCUSLESS_CONTAINER_TAGS = new Set([
|
|
5992
6079
|
"tr",
|
|
5993
6080
|
"td",
|
|
@@ -6035,7 +6122,10 @@ const isFocusForwardingFunctionBody = (body) => {
|
|
|
6035
6122
|
};
|
|
6036
6123
|
const resolveHandlerFunction = (attribute) => {
|
|
6037
6124
|
if (!attribute.value || !isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
|
|
6038
|
-
|
|
6125
|
+
return resolveHandlerFunctionExpression(attribute.value.expression);
|
|
6126
|
+
};
|
|
6127
|
+
const resolveHandlerFunctionExpression = (handlerExpression) => {
|
|
6128
|
+
let expression = handlerExpression;
|
|
6039
6129
|
if (isNodeOfType(expression, "Identifier")) {
|
|
6040
6130
|
const binding = findVariableInitializer(expression, expression.name);
|
|
6041
6131
|
if (!binding?.initializer) return null;
|
|
@@ -6134,18 +6224,22 @@ const clickEventsHaveKeyEvents = defineRule({
|
|
|
6134
6224
|
if (!HTML_TAGS.has(tag)) return;
|
|
6135
6225
|
if (tag === "label") return;
|
|
6136
6226
|
if (!FOCUSLESS_CONTAINER_TAGS.has(tag) && isInteractiveElement(tag, node)) return;
|
|
6227
|
+
const spreadEventValues = getTransparentSpreadEventValues(node.attributes, context.scopes);
|
|
6228
|
+
if (!spreadEventValues) return;
|
|
6137
6229
|
const onClick = hasJsxPropIgnoreCase(node.attributes, "onClick") ?? hasJsxPropIgnoreCase(node.attributes, "onClickCapture");
|
|
6138
|
-
|
|
6139
|
-
if (
|
|
6140
|
-
if (
|
|
6141
|
-
if (
|
|
6230
|
+
const spreadOnClickExpression = CLICK_HANDLERS.map((name) => spreadEventValues.get(name.toLowerCase())).find((expression) => expression !== void 0);
|
|
6231
|
+
if (!onClick && !spreadOnClickExpression) return;
|
|
6232
|
+
if (onClick && isPureEventBlockerHandler(onClick)) return;
|
|
6233
|
+
if (onClick && isFocusForwardingHandler(onClick)) return;
|
|
6234
|
+
const spreadHandlerFunction = spreadOnClickExpression ? resolveHandlerFunctionExpression(spreadOnClickExpression) : null;
|
|
6235
|
+
if (spreadHandlerFunction && (isFocusForwardingFunctionBody(spreadHandlerFunction.body ?? null) || containsBackdropDismissComparison(spreadHandlerFunction.body ?? null))) return;
|
|
6142
6236
|
if (hasCompositeItemRole(node)) return;
|
|
6143
6237
|
if (isHoverSelectionListItem(tag, node)) return;
|
|
6144
|
-
if (isBackdropDismissHandler(onClick)) return;
|
|
6238
|
+
if (onClick && isBackdropDismissHandler(onClick)) return;
|
|
6145
6239
|
if (containsKeyboardActivatableDescendant(node.parent)) return;
|
|
6146
6240
|
if (isHiddenFromScreenReader(node, context.settings)) return;
|
|
6147
6241
|
if (isPresentationRole(node)) return;
|
|
6148
|
-
if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
|
|
6242
|
+
if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
|
|
6149
6243
|
context.report({
|
|
6150
6244
|
node: node.name,
|
|
6151
6245
|
message: MESSAGE$57
|
|
@@ -7842,21 +7936,6 @@ const displayName = defineRule({
|
|
|
7842
7936
|
}
|
|
7843
7937
|
});
|
|
7844
7938
|
//#endregion
|
|
7845
|
-
//#region src/plugin/utils/get-static-property-key-name.ts
|
|
7846
|
-
const getStaticPropertyKeyName = (node, options = {}) => {
|
|
7847
|
-
if (!isNodeOfType(node, "Property")) return null;
|
|
7848
|
-
if (node.computed) {
|
|
7849
|
-
if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
|
|
7850
|
-
return null;
|
|
7851
|
-
}
|
|
7852
|
-
if (isNodeOfType(node.key, "Identifier")) return node.key.name;
|
|
7853
|
-
if (isNodeOfType(node.key, "Literal")) {
|
|
7854
|
-
if (typeof node.key.value === "string") return node.key.value;
|
|
7855
|
-
if (options.stringifyNonStringLiterals) return String(node.key.value);
|
|
7856
|
-
}
|
|
7857
|
-
return null;
|
|
7858
|
-
};
|
|
7859
|
-
//#endregion
|
|
7860
7939
|
//#region src/plugin/utils/read-static-boolean.ts
|
|
7861
7940
|
const readStaticBoolean = (node) => {
|
|
7862
7941
|
if (!node) return null;
|
|
@@ -7865,21 +7944,6 @@ const readStaticBoolean = (node) => {
|
|
|
7865
7944
|
return unwrappedNode.value;
|
|
7866
7945
|
};
|
|
7867
7946
|
//#endregion
|
|
7868
|
-
//#region src/plugin/utils/resolve-const-identifier-alias.ts
|
|
7869
|
-
const resolveConstIdentifierAlias = (identifier, scopes) => {
|
|
7870
|
-
if (!isNodeOfType(identifier, "Identifier")) return null;
|
|
7871
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
7872
|
-
let symbol = scopes.symbolFor(identifier);
|
|
7873
|
-
while (symbol?.kind === "const") {
|
|
7874
|
-
if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
7875
|
-
visitedSymbolIds.add(symbol.id);
|
|
7876
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
7877
|
-
if (!isNodeOfType(initializer, "Identifier")) return symbol;
|
|
7878
|
-
symbol = scopes.symbolFor(initializer);
|
|
7879
|
-
}
|
|
7880
|
-
return symbol;
|
|
7881
|
-
};
|
|
7882
|
-
//#endregion
|
|
7883
7947
|
//#region src/plugin/utils/is-react-api-call.ts
|
|
7884
7948
|
const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
|
|
7885
7949
|
const isImportedFromReact = (symbol) => {
|
|
@@ -9006,6 +9070,30 @@ const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, conte
|
|
|
9006
9070
|
}
|
|
9007
9071
|
return matchingBlocks.size > 0;
|
|
9008
9072
|
};
|
|
9073
|
+
const doMatchingNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context) => {
|
|
9074
|
+
const functionCfg = context.cfg.cfgFor(owner);
|
|
9075
|
+
if (!functionCfg) return false;
|
|
9076
|
+
const matchingBlocks = new Set(matchingNodes.flatMap((matchingNode) => {
|
|
9077
|
+
if (context.cfg.enclosingFunction(matchingNode) !== owner) return [];
|
|
9078
|
+
const matchingBlock = functionCfg.blockOf(matchingNode);
|
|
9079
|
+
return matchingBlock ? [matchingBlock] : [];
|
|
9080
|
+
}));
|
|
9081
|
+
if (matchingBlocks.size === 0) return false;
|
|
9082
|
+
const visitedBlocks = new Set([functionCfg.entry]);
|
|
9083
|
+
const pendingBlocks = [functionCfg.entry];
|
|
9084
|
+
while (pendingBlocks.length > 0) {
|
|
9085
|
+
const currentBlock = pendingBlocks.pop();
|
|
9086
|
+
if (!currentBlock) break;
|
|
9087
|
+
if (matchingBlocks.has(currentBlock)) continue;
|
|
9088
|
+
for (const edge of currentBlock.successors) {
|
|
9089
|
+
if (edge.to === functionCfg.exit) return false;
|
|
9090
|
+
if (visitedBlocks.has(edge.to)) continue;
|
|
9091
|
+
visitedBlocks.add(edge.to);
|
|
9092
|
+
pendingBlocks.push(edge.to);
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
return true;
|
|
9096
|
+
};
|
|
9009
9097
|
const removeSynchronouslyReleasedUsages = (callback, usages, context) => {
|
|
9010
9098
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return usages;
|
|
9011
9099
|
if (!isNodeOfType(callback.body, "BlockStatement")) return usages;
|
|
@@ -9206,6 +9294,83 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
|
|
|
9206
9294
|
});
|
|
9207
9295
|
return didCleanupFunctionMatch;
|
|
9208
9296
|
};
|
|
9297
|
+
const doesBoundCleanupReleaseUsage = (expression, usage, context) => {
|
|
9298
|
+
const callExpression = stripParenExpression(expression);
|
|
9299
|
+
if (!isNodeOfType(callExpression, "CallExpression")) return false;
|
|
9300
|
+
const bindCallee = stripParenExpression(callExpression.callee);
|
|
9301
|
+
if (!isNodeOfType(bindCallee, "MemberExpression") || bindCallee.computed || !isNodeOfType(bindCallee.property, "Identifier") || bindCallee.property.name !== "bind") return false;
|
|
9302
|
+
const releaseMember = stripParenExpression(bindCallee.object);
|
|
9303
|
+
if (!isNodeOfType(releaseMember, "MemberExpression") || releaseMember.computed || !isNodeOfType(releaseMember.property, "Identifier")) return false;
|
|
9304
|
+
const releaseReceiverKey = resolveExpressionKey$1(releaseMember.object, context);
|
|
9305
|
+
if (releaseReceiverKey === null || releaseReceiverKey !== resolveExpressionKey$1(callExpression.arguments?.[0], context)) return false;
|
|
9306
|
+
const releaseVerbName = releaseMember.property.name;
|
|
9307
|
+
if (usage.kind === "socket") return usage.handleKey === releaseReceiverKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
|
|
9308
|
+
return usage.kind === "subscribe" && usage.handleKey === releaseReceiverKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName));
|
|
9309
|
+
};
|
|
9310
|
+
const callbackReturnsCleanupForUsage = (callback, usage, context) => {
|
|
9311
|
+
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
|
|
9312
|
+
const doesReturnedValueReleaseUsage = (returnedValue) => {
|
|
9313
|
+
if (doesBoundCleanupReleaseUsage(returnedValue, usage, context)) return true;
|
|
9314
|
+
const cleanupFunction = resolveStableValue(returnedValue, context);
|
|
9315
|
+
if (cleanupFunction && doesBoundCleanupReleaseUsage(cleanupFunction, usage, context)) return true;
|
|
9316
|
+
return Boolean(cleanupFunction && isFunctionLike$1(cleanupFunction) && doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context));
|
|
9317
|
+
};
|
|
9318
|
+
if (!isNodeOfType(callback.body, "BlockStatement")) return doesReturnedValueReleaseUsage(stripParenExpression(callback.body));
|
|
9319
|
+
const matchingCleanupReturns = [];
|
|
9320
|
+
walkInsideStatementBlocks(callback.body, (child) => {
|
|
9321
|
+
if (isNodeOfType(child, "ReturnStatement") && child.argument && doesReturnedValueReleaseUsage(stripParenExpression(child.argument))) matchingCleanupReturns.push(child);
|
|
9322
|
+
});
|
|
9323
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
|
|
9324
|
+
};
|
|
9325
|
+
const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
|
|
9326
|
+
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression") || !isNodeOfType(callback.body, "BlockStatement")) return false;
|
|
9327
|
+
const functionCfg = context.cfg.cfgFor(callback);
|
|
9328
|
+
const usageBlock = functionCfg?.blockOf(usage.node);
|
|
9329
|
+
const usageStart = getRangeStart(usage.node);
|
|
9330
|
+
if (!functionCfg || !usageBlock || usageStart === null) return false;
|
|
9331
|
+
const findHandleGuard = (releaseCall) => {
|
|
9332
|
+
if (usage.handleKey === null) return null;
|
|
9333
|
+
let ancestor = releaseCall.parent;
|
|
9334
|
+
while (ancestor && ancestor !== callback.body) {
|
|
9335
|
+
if (isNodeOfType(ancestor, "IfStatement")) return ancestor.alternate === null && resolveExpressionKey$1(ancestor.test, context) === usage.handleKey && getRangeStart(ancestor) !== null && (getRangeStart(ancestor) ?? usageStart) < usageStart ? ancestor : null;
|
|
9336
|
+
ancestor = ancestor.parent;
|
|
9337
|
+
}
|
|
9338
|
+
return null;
|
|
9339
|
+
};
|
|
9340
|
+
const matchingReleaseAnchors = [];
|
|
9341
|
+
walkInsideStatementBlocks(callback.body, (child) => {
|
|
9342
|
+
if (!isNodeOfType(child, "CallExpression")) return;
|
|
9343
|
+
const releaseStart = getRangeStart(child);
|
|
9344
|
+
const handleGuard = findHandleGuard(child);
|
|
9345
|
+
if (releaseStart === null || releaseStart >= usageStart || functionCfg.blockOf(child) !== usageBlock && !handleGuard) return;
|
|
9346
|
+
if (doesReleaseCallMatchUsage(child, usage, context)) {
|
|
9347
|
+
matchingReleaseAnchors.push(handleGuard ?? child);
|
|
9348
|
+
return;
|
|
9349
|
+
}
|
|
9350
|
+
const helperFunction = resolveStableValue(child.callee, context);
|
|
9351
|
+
if (helperFunction && isFunctionLike$1(helperFunction) && doesCleanupFunctionReleaseUsage(helperFunction, usage, context)) matchingReleaseAnchors.push(handleGuard ?? child);
|
|
9352
|
+
});
|
|
9353
|
+
return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
|
|
9354
|
+
};
|
|
9355
|
+
const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
|
|
9356
|
+
const componentFunction = findEnclosingFunction(callback);
|
|
9357
|
+
if (!componentFunction || !isNodeOfType(componentFunction, "ArrowFunctionExpression") && !isNodeOfType(componentFunction, "FunctionExpression") && !isNodeOfType(componentFunction, "FunctionDeclaration")) return false;
|
|
9358
|
+
let didFindUnmountCleanup = false;
|
|
9359
|
+
walkAst(componentFunction.body, (child) => {
|
|
9360
|
+
if (didFindUnmountCleanup) return false;
|
|
9361
|
+
if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction) return;
|
|
9362
|
+
if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
|
|
9363
|
+
const dependencyList = child.arguments?.[1];
|
|
9364
|
+
if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
|
|
9365
|
+
const cleanupCallback = getEffectCallback(child);
|
|
9366
|
+
if (cleanupCallback && cleanupCallback !== callback && callbackReturnsCleanupForUsage(cleanupCallback, usage, context)) {
|
|
9367
|
+
didFindUnmountCleanup = true;
|
|
9368
|
+
return false;
|
|
9369
|
+
}
|
|
9370
|
+
});
|
|
9371
|
+
return didFindUnmountCleanup;
|
|
9372
|
+
};
|
|
9373
|
+
const hasSplitLifecycleCleanup = (callback, usage, context) => usage.handleKey !== null && hasRerunReleaseBeforeUsage(callback, usage, context) && hasStableUnmountCleanupForUsage(callback, usage, context);
|
|
9209
9374
|
const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
9210
9375
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
|
|
9211
9376
|
if (usage.kind === "subscribe" && findEnclosingFunction(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
|
|
@@ -9218,6 +9383,10 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
|
9218
9383
|
if (returnStart !== null && usageStart !== null && returnStart < usageStart) return;
|
|
9219
9384
|
const returnedValue = child.argument ? stripParenExpression(child.argument) : null;
|
|
9220
9385
|
if (!returnedValue) return;
|
|
9386
|
+
if (doesBoundCleanupReleaseUsage(returnedValue, usage, context)) {
|
|
9387
|
+
matchingCleanupReturns.push(child);
|
|
9388
|
+
return;
|
|
9389
|
+
}
|
|
9221
9390
|
if (usage.kind === "subscribe" && (returnedValue === usage.node || getRangeStart(returnedValue) !== null && getRangeStart(returnedValue) === getRangeStart(usage.node)) && isCleanupReturningSubscribeLikeCallExpression(returnedValue)) {
|
|
9222
9391
|
matchingCleanupReturns.push(child);
|
|
9223
9392
|
return;
|
|
@@ -9233,13 +9402,17 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
|
9233
9402
|
if (!context.scopes.symbolFor(returnedValue)?.initializer) return;
|
|
9234
9403
|
}
|
|
9235
9404
|
const cleanupFunction = resolveStableValue(returnedValue, context);
|
|
9405
|
+
if (cleanupFunction && doesBoundCleanupReleaseUsage(cleanupFunction, usage, context)) {
|
|
9406
|
+
matchingCleanupReturns.push(child);
|
|
9407
|
+
return;
|
|
9408
|
+
}
|
|
9236
9409
|
if (!cleanupFunction || !isFunctionLike$1(cleanupFunction)) return;
|
|
9237
9410
|
if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
|
|
9238
9411
|
});
|
|
9239
9412
|
return doMatchingNodesCoverEveryPathAfterUsage(resolveCleanupPathAnchor(usage.node, callback, context), matchingCleanupReturns, context);
|
|
9240
9413
|
};
|
|
9241
9414
|
const findFirstUsageWithoutCleanup = (callback, usages, context) => {
|
|
9242
|
-
for (const usage of usages) if (!effectHasCleanupForUsage(callback, usage, context)) return usage;
|
|
9415
|
+
for (const usage of usages) if (!effectHasCleanupForUsage(callback, usage, context) && !hasSplitLifecycleCleanup(callback, usage, context)) return usage;
|
|
9243
9416
|
return null;
|
|
9244
9417
|
};
|
|
9245
9418
|
const isLocalAbortControllerExpression = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
@@ -23044,61 +23217,73 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
|
|
|
23044
23217
|
"Array",
|
|
23045
23218
|
"BigInt",
|
|
23046
23219
|
"Boolean",
|
|
23220
|
+
"encodeURIComponent",
|
|
23047
23221
|
"Number",
|
|
23048
23222
|
"Object",
|
|
23049
23223
|
"String",
|
|
23050
23224
|
"parseFloat",
|
|
23051
|
-
"parseInt"
|
|
23225
|
+
"parseInt",
|
|
23226
|
+
"structuredClone"
|
|
23227
|
+
]);
|
|
23228
|
+
const PURE_GLOBAL_CONSTRUCTOR_NAMES = new Set(["Date", "Set"]);
|
|
23229
|
+
const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([
|
|
23230
|
+
["Array", new Set(["from"])],
|
|
23231
|
+
["JSON", new Set([
|
|
23232
|
+
"isRawJSON",
|
|
23233
|
+
"parse",
|
|
23234
|
+
"rawJSON",
|
|
23235
|
+
"stringify"
|
|
23236
|
+
])],
|
|
23237
|
+
["Math", new Set([
|
|
23238
|
+
"abs",
|
|
23239
|
+
"acos",
|
|
23240
|
+
"acosh",
|
|
23241
|
+
"asin",
|
|
23242
|
+
"asinh",
|
|
23243
|
+
"atan",
|
|
23244
|
+
"atan2",
|
|
23245
|
+
"atanh",
|
|
23246
|
+
"cbrt",
|
|
23247
|
+
"ceil",
|
|
23248
|
+
"clz32",
|
|
23249
|
+
"cos",
|
|
23250
|
+
"cosh",
|
|
23251
|
+
"exp",
|
|
23252
|
+
"floor",
|
|
23253
|
+
"fround",
|
|
23254
|
+
"hypot",
|
|
23255
|
+
"imul",
|
|
23256
|
+
"log",
|
|
23257
|
+
"log10",
|
|
23258
|
+
"log1p",
|
|
23259
|
+
"log2",
|
|
23260
|
+
"max",
|
|
23261
|
+
"min",
|
|
23262
|
+
"pow",
|
|
23263
|
+
"round",
|
|
23264
|
+
"sign",
|
|
23265
|
+
"sin",
|
|
23266
|
+
"sinh",
|
|
23267
|
+
"sqrt",
|
|
23268
|
+
"tan",
|
|
23269
|
+
"tanh",
|
|
23270
|
+
"trunc"
|
|
23271
|
+
])],
|
|
23272
|
+
["Object", new Set(["assign"])]
|
|
23052
23273
|
]);
|
|
23053
|
-
const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
|
|
23054
|
-
const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
|
|
23055
|
-
"isRawJSON",
|
|
23056
|
-
"parse",
|
|
23057
|
-
"rawJSON",
|
|
23058
|
-
"stringify"
|
|
23059
|
-
])], ["Math", new Set([
|
|
23060
|
-
"abs",
|
|
23061
|
-
"acos",
|
|
23062
|
-
"acosh",
|
|
23063
|
-
"asin",
|
|
23064
|
-
"asinh",
|
|
23065
|
-
"atan",
|
|
23066
|
-
"atan2",
|
|
23067
|
-
"atanh",
|
|
23068
|
-
"cbrt",
|
|
23069
|
-
"ceil",
|
|
23070
|
-
"clz32",
|
|
23071
|
-
"cos",
|
|
23072
|
-
"cosh",
|
|
23073
|
-
"exp",
|
|
23074
|
-
"floor",
|
|
23075
|
-
"fround",
|
|
23076
|
-
"hypot",
|
|
23077
|
-
"imul",
|
|
23078
|
-
"log",
|
|
23079
|
-
"log10",
|
|
23080
|
-
"log1p",
|
|
23081
|
-
"log2",
|
|
23082
|
-
"max",
|
|
23083
|
-
"min",
|
|
23084
|
-
"pow",
|
|
23085
|
-
"round",
|
|
23086
|
-
"sign",
|
|
23087
|
-
"sin",
|
|
23088
|
-
"sinh",
|
|
23089
|
-
"sqrt",
|
|
23090
|
-
"tan",
|
|
23091
|
-
"tanh",
|
|
23092
|
-
"trunc"
|
|
23093
|
-
])]]);
|
|
23094
23274
|
const PURE_MEMBER_TRANSFORM_NAMES = new Set([
|
|
23095
23275
|
"concat",
|
|
23096
23276
|
"filter",
|
|
23277
|
+
"flatMap",
|
|
23097
23278
|
"join",
|
|
23098
23279
|
"map",
|
|
23280
|
+
"reduce",
|
|
23281
|
+
"replace",
|
|
23282
|
+
"slice",
|
|
23099
23283
|
"split",
|
|
23100
23284
|
"toLowerCase",
|
|
23101
23285
|
"toString",
|
|
23286
|
+
"toSorted",
|
|
23102
23287
|
"toUpperCase",
|
|
23103
23288
|
"trim"
|
|
23104
23289
|
]);
|
|
@@ -23480,6 +23665,11 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
|
|
|
23480
23665
|
const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
|
|
23481
23666
|
return callbackSummary.isValid && !callbackSummary.canContinue;
|
|
23482
23667
|
}
|
|
23668
|
+
if (isNodeOfType(node, "NewExpression")) {
|
|
23669
|
+
const callee = stripParenExpression(node.callee);
|
|
23670
|
+
if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || environment.parameterIndices.has(callee.name) || environment.recursiveNames.has(callee.name) || environment.shadowedGlobalNames.has(callee.name)) return false;
|
|
23671
|
+
return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
|
|
23672
|
+
}
|
|
23483
23673
|
if (isNodeOfType(node, "CallExpression")) {
|
|
23484
23674
|
const callee = stripParenExpression(node.callee);
|
|
23485
23675
|
const calleeRoot = getMemberRoot(callee);
|
|
@@ -23682,7 +23872,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
23682
23872
|
}
|
|
23683
23873
|
const callee = stripParenExpression(node.callee);
|
|
23684
23874
|
const calleeRoot = getMemberRoot(callee);
|
|
23685
|
-
const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(
|
|
23875
|
+
const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(callee, "MemberExpression") && isNodeOfType(calleeRoot, "Identifier") && getIdentifierBindingIdentity(analysis, calleeRoot) === null && PURE_HELPER_NAMESPACE_MEMBER_NAMES.get(calleeRoot.name)?.has(getStaticMemberName$1(callee) ?? "") === true;
|
|
23686
23876
|
const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName$1(callee) ?? "");
|
|
23687
23877
|
if (isPureGlobalCall || isPureMemberTransform) {
|
|
23688
23878
|
if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
@@ -23735,6 +23925,15 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
|
|
|
23735
23925
|
}
|
|
23736
23926
|
return evidence;
|
|
23737
23927
|
}
|
|
23928
|
+
if (isNodeOfType(node, "NewExpression")) {
|
|
23929
|
+
const callee = stripParenExpression(node.callee);
|
|
23930
|
+
if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || getIdentifierBindingIdentity(analysis, callee) !== null) {
|
|
23931
|
+
evidence.hasUnknownSource = true;
|
|
23932
|
+
return evidence;
|
|
23933
|
+
}
|
|
23934
|
+
for (const argument of node.arguments ?? []) mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
|
|
23935
|
+
return evidence;
|
|
23936
|
+
}
|
|
23738
23937
|
if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
|
|
23739
23938
|
evidence.hasUnknownSource = true;
|
|
23740
23939
|
return evidence;
|
|
@@ -28284,20 +28483,31 @@ const noDocumentStartViewTransition = defineRule({
|
|
|
28284
28483
|
} })
|
|
28285
28484
|
});
|
|
28286
28485
|
//#endregion
|
|
28486
|
+
//#region src/plugin/utils/get-static-property-name.ts
|
|
28487
|
+
const getStaticPropertyName = (memberExpression) => {
|
|
28488
|
+
const property = memberExpression.property;
|
|
28489
|
+
if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
|
|
28490
|
+
if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
|
|
28491
|
+
if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
|
|
28492
|
+
return null;
|
|
28493
|
+
};
|
|
28494
|
+
//#endregion
|
|
28287
28495
|
//#region src/plugin/rules/js-performance/no-document-write.ts
|
|
28288
28496
|
const MESSAGE$24 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
|
|
28289
28497
|
const WRITE_METHODS = new Set(["write", "writeln"]);
|
|
28498
|
+
const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
|
|
28290
28499
|
const noDocumentWrite = defineRule({
|
|
28291
28500
|
id: "no-document-write",
|
|
28292
28501
|
title: "document.write/writeln",
|
|
28293
28502
|
severity: "warn",
|
|
28294
28503
|
recommendation: "Don't use `document.write()`/`document.writeln()`. Append DOM nodes or set `innerHTML`/`textContent` on a specific element instead.",
|
|
28295
28504
|
create: (context) => ({ CallExpression(node) {
|
|
28296
|
-
const callee = node.callee;
|
|
28297
|
-
if (!isNodeOfType(callee, "MemberExpression")
|
|
28505
|
+
const callee = stripParenExpression(node.callee);
|
|
28506
|
+
if (!isNodeOfType(callee, "MemberExpression")) return;
|
|
28298
28507
|
const receiver = stripParenExpression(callee.object);
|
|
28299
|
-
if (!isNodeOfType(receiver, "Identifier") || receiver
|
|
28300
|
-
|
|
28508
|
+
if (!isNodeOfType(receiver, "Identifier") || !isGlobalDocumentReference(receiver, context)) return;
|
|
28509
|
+
const methodName = getStaticPropertyName(callee);
|
|
28510
|
+
if (methodName === null || !WRITE_METHODS.has(methodName)) return;
|
|
28301
28511
|
context.report({
|
|
28302
28512
|
node,
|
|
28303
28513
|
message: MESSAGE$24
|
|
@@ -29104,6 +29314,14 @@ const noEffectWithFreshDeps = defineRule({
|
|
|
29104
29314
|
//#endregion
|
|
29105
29315
|
//#region src/plugin/rules/security/no-eval.ts
|
|
29106
29316
|
const SANDBOX_SURFACE_PATH_PATTERN = /(?:^|[/-])sandbox(?:$|[/-])|(?:^|\/)[\w.]+-sandbox(?:\/|\.[cm]?[jt]sx?$)/i;
|
|
29317
|
+
const getExecutableGlobalName = (node, context) => {
|
|
29318
|
+
const expression = stripParenExpression(node);
|
|
29319
|
+
if (isNodeOfType(expression, "Identifier")) return context.scopes.isGlobalReference(expression) ? expression.name : null;
|
|
29320
|
+
if (!isNodeOfType(expression, "MemberExpression")) return null;
|
|
29321
|
+
const receiver = stripParenExpression(expression.object);
|
|
29322
|
+
if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "globalThis" || !context.scopes.isGlobalReference(receiver)) return null;
|
|
29323
|
+
return getStaticPropertyName(expression);
|
|
29324
|
+
};
|
|
29107
29325
|
const noEval = defineRule({
|
|
29108
29326
|
id: "no-eval",
|
|
29109
29327
|
title: "eval() runs untrusted code strings",
|
|
@@ -29113,20 +29331,21 @@ const noEval = defineRule({
|
|
|
29113
29331
|
if (SANDBOX_SURFACE_PATH_PATTERN.test(normalizeFilename(context.filename ?? ""))) return {};
|
|
29114
29332
|
return {
|
|
29115
29333
|
CallExpression(node) {
|
|
29116
|
-
|
|
29334
|
+
const executableGlobalName = getExecutableGlobalName(node.callee, context);
|
|
29335
|
+
if (executableGlobalName === "eval") {
|
|
29117
29336
|
context.report({
|
|
29118
29337
|
node,
|
|
29119
29338
|
message: "eval() is a code-injection vulnerability: it runs any string as code."
|
|
29120
29339
|
});
|
|
29121
29340
|
return;
|
|
29122
29341
|
}
|
|
29123
|
-
if (
|
|
29342
|
+
if ((executableGlobalName === "setTimeout" || executableGlobalName === "setInterval") && isNodeOfType(node.arguments?.[0], "Literal") && typeof node.arguments[0].value === "string") context.report({
|
|
29124
29343
|
node,
|
|
29125
|
-
message: `Passing a string to ${
|
|
29344
|
+
message: `Passing a string to ${executableGlobalName}() is a code-injection vulnerability, since it runs that string as code.`
|
|
29126
29345
|
});
|
|
29127
29346
|
},
|
|
29128
29347
|
NewExpression(node) {
|
|
29129
|
-
if (
|
|
29348
|
+
if (getExecutableGlobalName(node.callee, context) === "Function") {
|
|
29130
29349
|
const onlyArgument = node.arguments.length === 1 ? node.arguments[0] : void 0;
|
|
29131
29350
|
if (isNodeOfType(onlyArgument, "Literal") && typeof onlyArgument.value === "string" && onlyArgument.value.trim() === "return this") return;
|
|
29132
29351
|
context.report({
|
|
@@ -34843,6 +35062,14 @@ const isHandlerBagArgument = (analysis, argument) => {
|
|
|
34843
35062
|
};
|
|
34844
35063
|
const getFunctionalUpdaterDataRefs = (analysis, updater) => getDownstreamRefs(analysis, updater).filter((updaterRef) => !updaterRef.resolved?.defs.some((def) => def.type === "Parameter" && def.node === updater));
|
|
34845
35064
|
const HOOK_NAME_PATTERN$1 = /^use[A-Z0-9]/;
|
|
35065
|
+
const EXTERNAL_SUBSCRIPTION_HOOK_NAMES = new Set([
|
|
35066
|
+
"useIntersectionObserver",
|
|
35067
|
+
"useMatchMedia",
|
|
35068
|
+
"useMediaQuery",
|
|
35069
|
+
"useResizeObserver",
|
|
35070
|
+
"useVisibility",
|
|
35071
|
+
"useWindowSize"
|
|
35072
|
+
]);
|
|
34846
35073
|
const isParentWiredHookResultRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
|
|
34847
35074
|
const node = def.node;
|
|
34848
35075
|
if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
|
|
@@ -34865,6 +35092,19 @@ const isParentWiredHookCalleeRef = (analysis, ref) => {
|
|
|
34865
35092
|
if (!parent || !isNodeOfType(parent, "CallExpression") || parent.callee !== identifier) return false;
|
|
34866
35093
|
return (parent.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isProp(analysis, downstreamRef)));
|
|
34867
35094
|
};
|
|
35095
|
+
const isExternalSubscriptionHookRef = (ref) => {
|
|
35096
|
+
const identifier = ref.identifier;
|
|
35097
|
+
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
35098
|
+
if (EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(identifier.name) && isCalleePosition(identifier)) return true;
|
|
35099
|
+
return Boolean(ref.resolved?.defs.some((def) => {
|
|
35100
|
+
const node = def.node;
|
|
35101
|
+
if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
|
|
35102
|
+
const initializer = stripParenExpression(node.init);
|
|
35103
|
+
if (!isNodeOfType(initializer, "CallExpression")) return false;
|
|
35104
|
+
const callee = stripParenExpression(initializer.callee);
|
|
35105
|
+
return isNodeOfType(callee, "Identifier") && EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(callee.name);
|
|
35106
|
+
}));
|
|
35107
|
+
};
|
|
34868
35108
|
const isImportBindingRef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "ImportBinding"));
|
|
34869
35109
|
const isCalleePosition = (identifier) => {
|
|
34870
35110
|
const parent = identifier.parent;
|
|
@@ -34926,10 +35166,11 @@ const noPassDataToParent = defineRule({
|
|
|
34926
35166
|
if (argumentRef && resolveToFunction(argumentRef)) return [];
|
|
34927
35167
|
}
|
|
34928
35168
|
return getDownstreamRefs(analysis, argument);
|
|
34929
|
-
}).flatMap((argumentRef) => getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
|
|
35169
|
+
}).flatMap((argumentRef) => isExternallyDrivenState(analysis, argumentRef) ? [] : getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
|
|
34930
35170
|
if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
|
|
34931
35171
|
if (!argsUpstreamRefs.some((argRef) => {
|
|
34932
35172
|
if (isUseStateIdentifier(argRef.identifier)) return false;
|
|
35173
|
+
if (isExternalSubscriptionHookRef(argRef)) return false;
|
|
34933
35174
|
if (isProp(analysis, argRef)) return false;
|
|
34934
35175
|
if (isUseRefIdentifier(argRef.identifier)) return false;
|
|
34935
35176
|
if (isRefCurrent(argRef)) return false;
|
|
@@ -54687,12 +54928,6 @@ const webhookSignatureRisk = defineRule({
|
|
|
54687
54928
|
//#region src/plugin/rules/zod/utils/zod-ast.ts
|
|
54688
54929
|
const ZOD_MODULE = "zod";
|
|
54689
54930
|
const ZOD_MODULE_SOURCES = [ZOD_MODULE];
|
|
54690
|
-
const getStaticPropertyName = (member) => {
|
|
54691
|
-
const property = member.property;
|
|
54692
|
-
if (!member.computed && isNodeOfType(property, "Identifier")) return property.name;
|
|
54693
|
-
if (member.computed && isNodeOfType(property, "Literal") && typeof property.value === "string") return property.value;
|
|
54694
|
-
return null;
|
|
54695
|
-
};
|
|
54696
54931
|
const importInfoCache = /* @__PURE__ */ new WeakMap();
|
|
54697
54932
|
const getImportInfoForIdentifier = (identifier) => {
|
|
54698
54933
|
if (importInfoCache.has(identifier)) return importInfoCache.get(identifier) ?? null;
|