oxlint-plugin-react-doctor 0.7.5-dev.70eff9a → 0.7.5-dev.76cd6be

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.
Files changed (2) hide show
  1. package/dist/index.js +184 -733
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -397,7 +397,6 @@ const isProductionFilePath = (relativePath, sourceFilePattern) => {
397
397
  //#endregion
398
398
  //#region src/plugin/rules/security-scan/utils/is-production-source-path.ts
399
399
  const isProductionSourcePath = (relativePath) => {
400
- if (/\.d\.[cm]?[jt]s$/i.test(relativePath)) return false;
401
400
  return isProductionFilePath(relativePath, SOURCE_FILE_PATTERN);
402
401
  };
403
402
  //#endregion
@@ -5179,10 +5178,8 @@ const STORAGE_GLOBALS = new Set([
5179
5178
  const SENSITIVE_KEY_PATTERN = /token|jwt|secret|password|passwd|credential|api[-_]?key|bearer|private[-_]?key/i;
5180
5179
  const NON_AUTH_TOKEN_PATTERN = /csrf|xsrf|device|fcm|apns|push|design|tokeniz|syntax|css|theme|color/i;
5181
5180
  const STRONG_AUTH_KEY_PATTERN = /jwt|secret|password|passwd|credential|private[-_]?key|api[-_]?key|bearer|access[-_]?token|refresh[-_]?token|auth[-_]?token|id[-_]?token|session/i;
5182
- const PRODUCT_API_KEY_RECORDS_PATTERN = /(?:^|[._:-])(?:created|saved|integration|mailing)[-_]?api[-_]?keys$/i;
5183
5181
  const PRODUCT_API_KEY_COLLECTION_PATTERN = /[._:-](?:created|generated|saved)[-_]?api[-_]?keys$/i;
5184
5182
  const isAuthCredentialKey = (key) => {
5185
- if (PRODUCT_API_KEY_RECORDS_PATTERN.test(key)) return false;
5186
5183
  if (!SENSITIVE_KEY_PATTERN.test(key)) return false;
5187
5184
  if (PRODUCT_API_KEY_COLLECTION_PATTERN.test(key)) return false;
5188
5185
  if (NON_AUTH_TOKEN_PATTERN.test(key) && !STRONG_AUTH_KEY_PATTERN.test(key)) return false;
@@ -5934,21 +5931,6 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5934
5931
  }
5935
5932
  });
5936
5933
  //#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
5952
5934
  //#region src/plugin/utils/is-presentation-role.ts
5953
5935
  const isPresentationRole = (openingElement) => {
5954
5936
  const roleAttribute = hasJsxPropIgnoreCase(openingElement.attributes, "role");
@@ -5993,21 +5975,6 @@ const isPureEventBlockerHandler = (attribute) => {
5993
5975
  return false;
5994
5976
  };
5995
5977
  //#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
6011
5978
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
6012
5979
  const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6013
5980
  const KEY_HANDLERS = [
@@ -6018,63 +5985,6 @@ const KEY_HANDLERS = [
6018
5985
  "onKeyDownCapture",
6019
5986
  "onKeyPressCapture"
6020
5987
  ];
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
- };
6078
5988
  const FOCUSLESS_CONTAINER_TAGS = new Set([
6079
5989
  "tr",
6080
5990
  "td",
@@ -6122,10 +6032,7 @@ const isFocusForwardingFunctionBody = (body) => {
6122
6032
  };
6123
6033
  const resolveHandlerFunction = (attribute) => {
6124
6034
  if (!attribute.value || !isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
6125
- return resolveHandlerFunctionExpression(attribute.value.expression);
6126
- };
6127
- const resolveHandlerFunctionExpression = (handlerExpression) => {
6128
- let expression = handlerExpression;
6035
+ let expression = attribute.value.expression;
6129
6036
  if (isNodeOfType(expression, "Identifier")) {
6130
6037
  const binding = findVariableInitializer(expression, expression.name);
6131
6038
  if (!binding?.initializer) return null;
@@ -6224,22 +6131,18 @@ const clickEventsHaveKeyEvents = defineRule({
6224
6131
  if (!HTML_TAGS.has(tag)) return;
6225
6132
  if (tag === "label") return;
6226
6133
  if (!FOCUSLESS_CONTAINER_TAGS.has(tag) && isInteractiveElement(tag, node)) return;
6227
- const spreadEventValues = getTransparentSpreadEventValues(node.attributes, context.scopes);
6228
- if (!spreadEventValues) return;
6229
6134
  const onClick = hasJsxPropIgnoreCase(node.attributes, "onClick") ?? hasJsxPropIgnoreCase(node.attributes, "onClickCapture");
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;
6135
+ if (!onClick) return;
6136
+ if (isPureEventBlockerHandler(onClick)) return;
6137
+ if (isFocusForwardingHandler(onClick)) return;
6138
+ if (hasJsxSpreadAttribute$1(node.attributes)) return;
6236
6139
  if (hasCompositeItemRole(node)) return;
6237
6140
  if (isHoverSelectionListItem(tag, node)) return;
6238
- if (onClick && isBackdropDismissHandler(onClick)) return;
6141
+ if (isBackdropDismissHandler(onClick)) return;
6239
6142
  if (containsKeyboardActivatableDescendant(node.parent)) return;
6240
6143
  if (isHiddenFromScreenReader(node, context.settings)) return;
6241
6144
  if (isPresentationRole(node)) return;
6242
- if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6145
+ if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
6243
6146
  context.report({
6244
6147
  node: node.name,
6245
6148
  message: MESSAGE$57
@@ -6965,10 +6868,11 @@ const getTemplateInterpolations = (valueTail) => {
6965
6868
  const interpolations = valueTail.slice(1, closingBacktickIndex).match(/\$\{[^}]*\}/g);
6966
6869
  return interpolations === null ? "" : interpolations.join(" ");
6967
6870
  };
6968
- const getIdentifierDeclarationInitializer = (identifier, sinkIndex, fileContent) => {
6969
- const declaration = findVisibleIdentifierDeclaration(identifier, sinkIndex, fileContent);
6970
- if (declaration === null) return null;
6971
- return fileContent.slice(declaration.initializerStartIndex, declaration.initializerStartIndex + STATIC_TEMPLATE_MAX_CHARS);
6871
+ const getIdentifierDeclarationInitializer = (identifier, fileContent) => {
6872
+ const declarationMatch = new RegExp(`(?:const|let|var)\\s+${escapeRegExp(identifier)}\\s*(?::[^=\\n]{0,120})?=\\s*`).exec(fileContent);
6873
+ if (declarationMatch === null) return null;
6874
+ const initializerStartIndex = declarationMatch.index + declarationMatch[0].length;
6875
+ return fileContent.slice(initializerStartIndex, initializerStartIndex + STATIC_TEMPLATE_MAX_CHARS);
6972
6876
  };
6973
6877
  const isPureStringLiteralConcat = (initializerText) => {
6974
6878
  if (!/^["']/.test(initializerText)) return false;
@@ -7038,153 +6942,6 @@ const isAllOperandsDomContentConcat = (valueExpression) => {
7038
6942
  return !HTML_TAINT_PATTERN.test(withoutCast.replace(DOM_CONTENT_SOURCE_VALUE_PATTERN, ""));
7039
6943
  });
7040
6944
  };
7041
- const findMatchingBraceIndex = (fileContent, openingBraceIndex) => {
7042
- let depth = 0;
7043
- let quote = null;
7044
- for (let index = openingBraceIndex; index < fileContent.length; index += 1) {
7045
- const character = fileContent[index];
7046
- if (quote !== null) {
7047
- if (character === quote && fileContent[index - 1] !== "\\") quote = null;
7048
- continue;
7049
- }
7050
- if (character === "\"" || character === "'" || character === "`") {
7051
- quote = character;
7052
- continue;
7053
- }
7054
- if (character === "{") depth += 1;
7055
- if (character === "}") {
7056
- depth -= 1;
7057
- if (depth === 0) return index;
7058
- }
7059
- }
7060
- return fileContent.length;
7061
- };
7062
- const findContainingBlockEndIndex = (fileContent, targetIndex) => {
7063
- const openingBraceIndexes = [];
7064
- let quote = null;
7065
- let isLineComment = false;
7066
- let isBlockComment = false;
7067
- for (let index = 0; index < targetIndex; index += 1) {
7068
- const character = fileContent[index];
7069
- const nextCharacter = fileContent[index + 1];
7070
- if (isLineComment) {
7071
- if (character === "\n") isLineComment = false;
7072
- continue;
7073
- }
7074
- if (isBlockComment) {
7075
- if (character === "*" && nextCharacter === "/") {
7076
- isBlockComment = false;
7077
- index += 1;
7078
- }
7079
- continue;
7080
- }
7081
- if (quote !== null) {
7082
- if (character === quote && fileContent[index - 1] !== "\\") quote = null;
7083
- continue;
7084
- }
7085
- if (character === "/" && nextCharacter === "/") {
7086
- isLineComment = true;
7087
- index += 1;
7088
- continue;
7089
- }
7090
- if (character === "/" && nextCharacter === "*") {
7091
- isBlockComment = true;
7092
- index += 1;
7093
- continue;
7094
- }
7095
- if (character === "\"" || character === "'" || character === "`") {
7096
- quote = character;
7097
- continue;
7098
- }
7099
- if (character === "{") openingBraceIndexes.push(index);
7100
- if (character === "}") openingBraceIndexes.pop();
7101
- }
7102
- const openingBraceIndex = openingBraceIndexes.at(-1);
7103
- return openingBraceIndex === void 0 ? fileContent.length : findMatchingBraceIndex(fileContent, openingBraceIndex);
7104
- };
7105
- const findVisibleIdentifierDeclaration = (identifier, sinkIndex, fileContent) => {
7106
- const initializerPattern = new RegExp(`(?:const|let|var)\\s+${escapeRegExp(identifier)}\\s*(?::[^=\\n]*)?=\\s*([^;\\n]+)`, "g");
7107
- let nearestDeclaration = null;
7108
- let nearestDeclarationIndex = -1;
7109
- for (const match of fileContent.matchAll(initializerPattern)) {
7110
- const declarationIndex = match.index;
7111
- if (declarationIndex === void 0 || declarationIndex >= sinkIndex || declarationIndex <= nearestDeclarationIndex || findContainingBlockEndIndex(fileContent, declarationIndex) < sinkIndex) continue;
7112
- nearestDeclarationIndex = declarationIndex;
7113
- const initializer = match[1];
7114
- if (initializer === void 0) continue;
7115
- nearestDeclaration = {
7116
- initializer,
7117
- initializerStartIndex: declarationIndex + match[0].length - initializer.length
7118
- };
7119
- }
7120
- return nearestDeclaration;
7121
- };
7122
- const findContainingFunctionParameterSource = (identifier, sinkIndex, fileContent) => {
7123
- const patterns = [/function\s+([\w$]+)\s*\(([^)]*)\)\s*\{/g, /(?:const|let|var)\s+([\w$]+)\s*=\s*(?:async\s*)?\(([^)]*)\)\s*=>\s*\{/g];
7124
- let closestSource = null;
7125
- let closestStartIndex = -1;
7126
- for (const pattern of patterns) for (const match of fileContent.matchAll(pattern)) {
7127
- const matchIndex = match.index;
7128
- if (matchIndex === void 0 || matchIndex >= sinkIndex || matchIndex < closestStartIndex) continue;
7129
- if (findMatchingBraceIndex(fileContent, matchIndex + match[0].lastIndexOf("{")) < sinkIndex) continue;
7130
- const parameterIndex = (match[2] ?? "").split(",").findIndex((parameter) => parameter.trim().match(/^(?:\.\.\.)?([\w$]+)/)?.[1] === identifier);
7131
- if (parameterIndex < 0) continue;
7132
- closestStartIndex = matchIndex;
7133
- closestSource = {
7134
- functionName: match[1] ?? "",
7135
- parameterIndex
7136
- };
7137
- }
7138
- return closestSource;
7139
- };
7140
- const splitTopLevelArguments = (argumentText) => {
7141
- const argumentsList = [];
7142
- let startIndex = 0;
7143
- let depth = 0;
7144
- let quote = null;
7145
- for (let index = 0; index < argumentText.length; index += 1) {
7146
- const character = argumentText[index];
7147
- if (quote !== null) {
7148
- if (character === quote && argumentText[index - 1] !== "\\") quote = null;
7149
- continue;
7150
- }
7151
- if (character === "\"" || character === "'" || character === "`") {
7152
- quote = character;
7153
- continue;
7154
- }
7155
- if (character === "(" || character === "[" || character === "{") depth += 1;
7156
- if (character === ")" || character === "]" || character === "}") depth -= 1;
7157
- if (character === "," && depth === 0) {
7158
- argumentsList.push(argumentText.slice(startIndex, index).trim());
7159
- startIndex = index + 1;
7160
- }
7161
- }
7162
- argumentsList.push(argumentText.slice(startIndex).trim());
7163
- return argumentsList;
7164
- };
7165
- const isHtmlTainted = (expression, fileContent, sinkIndex, visitedIdentifiers) => {
7166
- const trimmedExpression = expression.trim();
7167
- if (STRING_LITERAL_VALUE_PATTERN.test(`${trimmedExpression};`)) return false;
7168
- if (MODULE_CONSTANT_VALUE_PATTERN.test(`${trimmedExpression};`)) return false;
7169
- if (SANITIZER_PATTERN.test(trimmedExpression)) return false;
7170
- if (ENV_CONFIG_VALUE_PATTERN.test(trimmedExpression)) return false;
7171
- if (I18N_VALUE_PATTERN.test(trimmedExpression)) return false;
7172
- if (ESCAPING_SERIALIZER_CALL_PATTERN.test(trimmedExpression)) return false;
7173
- if (HTML_TAINT_PATTERN.test(trimmedExpression)) return true;
7174
- const identifier = trimmedExpression.match(/^([\w$]+)\s*(?:[;,})\n]|$)/)?.[1];
7175
- if (identifier === void 0 || visitedIdentifiers.has(identifier)) return false;
7176
- visitedIdentifiers.add(identifier);
7177
- const declaration = findVisibleIdentifierDeclaration(identifier, sinkIndex, fileContent);
7178
- if (declaration !== null && isHtmlTainted(declaration.initializer, fileContent, sinkIndex, visitedIdentifiers)) return true;
7179
- const parameterSource = findContainingFunctionParameterSource(identifier, sinkIndex, fileContent);
7180
- if (parameterSource === null || parameterSource.functionName.length === 0) return false;
7181
- const callPattern = new RegExp(`\\b${escapeRegExp(parameterSource.functionName)}\\s*\\(([^)]*)\\)`, "g");
7182
- for (const callMatch of fileContent.matchAll(callPattern)) {
7183
- const argument = splitTopLevelArguments(callMatch[1] ?? "")[parameterSource.parameterIndex];
7184
- if (argument !== void 0 && isHtmlTainted(argument, fileContent, sinkIndex, new Set(visitedIdentifiers))) return true;
7185
- }
7186
- return false;
7187
- };
7188
6945
  const dangerousHtmlSink = defineRule({
7189
6946
  id: "dangerous-html-sink",
7190
6947
  title: "HTML injection sink with dynamic content",
@@ -7211,7 +6968,6 @@ const dangerousHtmlSink = defineRule({
7211
6968
  const valueTail = fullValueTail.slice(0, VALUE_EXPRESSION_MAX_CHARS);
7212
6969
  const terminatorIndex = valueTail.search(/[;}]/);
7213
6970
  const valueExpression = terminatorIndex >= 0 ? valueTail.slice(0, terminatorIndex + 1) : valueTail;
7214
- const sinkIndex = lines.slice(0, lineIndex).join("\n").length + (lineIndex > 0 ? 1 : 0) + line.search(DANGEROUS_HTML_PATTERN);
7215
6971
  if (STRING_LITERAL_VALUE_PATTERN.test(valueExpression)) continue;
7216
6972
  if (MODULE_CONSTANT_VALUE_PATTERN.test(valueExpression)) continue;
7217
6973
  if (DOM_CONTENT_SOURCE_VALUE_PATTERN.test(valueExpression) && !valueExpression.includes("+")) {
@@ -7223,7 +6979,7 @@ const dangerousHtmlSink = defineRule({
7223
6979
  let templateInterpolations = getTemplateInterpolations(longValueTail ?? fullValueTail);
7224
6980
  const valueIdentifier = BARE_IDENTIFIER_VALUE_PATTERN.test(valueExpression) || MEMBER_OR_INDEX_ACCESS_VALUE_PATTERN.test(valueExpression) || IDENTIFIER_WITH_LITERAL_FALLBACK_PATTERN.test(valueExpression) ? valueExpression.match(/^[\w$]+/)?.[0] : void 0;
7225
6981
  if (templateInterpolations === null && valueIdentifier !== void 0 && BARE_IDENTIFIER_VALUE_PATTERN.test(valueExpression)) {
7226
- const declarationInitializer = getIdentifierDeclarationInitializer(valueIdentifier, sinkIndex, file.content);
6982
+ const declarationInitializer = getIdentifierDeclarationInitializer(valueIdentifier, file.content);
7227
6983
  if (declarationInitializer !== null) {
7228
6984
  if (isPureStringLiteralConcat(declarationInitializer)) continue;
7229
6985
  templateInterpolations = getTemplateInterpolations(declarationInitializer);
@@ -7234,7 +6990,7 @@ const dangerousHtmlSink = defineRule({
7234
6990
  if (SANITIZER_PATTERN.test(judgedExpression)) continue;
7235
6991
  if (ENV_CONFIG_VALUE_PATTERN.test(judgedExpression)) continue;
7236
6992
  if (I18N_VALUE_PATTERN.test(judgedExpression)) continue;
7237
- if (!isHtmlTainted(judgedExpression, file.content, sinkIndex, /* @__PURE__ */ new Set())) continue;
6993
+ if (!HTML_TAINT_PATTERN.test(judgedExpression)) continue;
7238
6994
  if (ESCAPING_SERIALIZER_CALL_PATTERN.test(valueExpression)) continue;
7239
6995
  if (/highlighted/i.test(valueExpression)) continue;
7240
6996
  if (/highlight/i.test(valueExpression) && HIGHLIGHTER_LIBRARY_PATTERN.test(file.content)) continue;
@@ -7681,7 +7437,7 @@ const containsJsx$1 = (root) => {
7681
7437
  containsJsxCache.set(root, found);
7682
7438
  return found;
7683
7439
  };
7684
- const getStaticMemberName$2 = (node) => {
7440
+ const getStaticMemberName$1 = (node) => {
7685
7441
  if (!isNodeOfType(node, "MemberExpression")) return null;
7686
7442
  if (!node.computed && isNodeOfType(node.property, "Identifier")) return node.property.name;
7687
7443
  if (node.computed && isNodeOfType(node.property, "Literal") && typeof node.property.value === "string") return node.property.value;
@@ -7695,7 +7451,7 @@ const getAssignedName = (node) => {
7695
7451
  if (isNodeOfType(parent, "AssignmentExpression")) {
7696
7452
  const left = parent.left;
7697
7453
  if (isNodeOfType(left, "Identifier")) return left.name;
7698
- if (isNodeOfType(left, "MemberExpression")) return getStaticMemberName$2(left);
7454
+ if (isNodeOfType(left, "MemberExpression")) return getStaticMemberName$1(left);
7699
7455
  }
7700
7456
  return null;
7701
7457
  };
@@ -7703,20 +7459,20 @@ const isModuleExportsAssignment = (node) => {
7703
7459
  const parent = node.parent;
7704
7460
  if (!parent || !isNodeOfType(parent, "AssignmentExpression")) return false;
7705
7461
  const left = parent.left;
7706
- return isNodeOfType(left, "MemberExpression") && isNodeOfType(left.object, "Identifier") && left.object.name === "module" && getStaticMemberName$2(left) === "exports";
7462
+ return isNodeOfType(left, "MemberExpression") && isNodeOfType(left.object, "Identifier") && left.object.name === "module" && getStaticMemberName$1(left) === "exports";
7707
7463
  };
7708
7464
  const isCreateClassLikeCall = (node) => {
7709
7465
  if (!isNodeOfType(node, "CallExpression")) return false;
7710
7466
  if (isEs5Component(node)) return true;
7711
7467
  const callee = node.callee;
7712
- if (isNodeOfType(callee, "MemberExpression")) return getStaticMemberName$2(callee) === "createClass";
7468
+ if (isNodeOfType(callee, "MemberExpression")) return getStaticMemberName$1(callee) === "createClass";
7713
7469
  return false;
7714
7470
  };
7715
- const isCreateContextCall$1 = (node) => {
7471
+ const isCreateContextCall = (node) => {
7716
7472
  if (!isNodeOfType(node, "CallExpression")) return false;
7717
7473
  const callee = node.callee;
7718
7474
  if (isNodeOfType(callee, "Identifier")) return callee.name === "createContext";
7719
- return isNodeOfType(callee, "MemberExpression") && getStaticMemberName$2(callee) === "createContext";
7475
+ return isNodeOfType(callee, "MemberExpression") && getStaticMemberName$1(callee) === "createContext";
7720
7476
  };
7721
7477
  const isNamedFunctionLike = (node) => (isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "FunctionDeclaration")) && Boolean(node.id?.name);
7722
7478
  const firstCallArgument = (node) => {
@@ -7774,7 +7530,7 @@ const memberExpressionPath = (node) => {
7774
7530
  if (isNodeOfType(node, "Identifier")) return [node.name];
7775
7531
  if (!isNodeOfType(node, "MemberExpression")) return [];
7776
7532
  const objectPath = memberExpressionPath(node.object);
7777
- const propertyName = getStaticMemberName$2(node);
7533
+ const propertyName = getStaticMemberName$1(node);
7778
7534
  return propertyName ? [...objectPath, propertyName] : objectPath;
7779
7535
  };
7780
7536
  const displayNameAssignmentIndexCache = /* @__PURE__ */ new WeakMap();
@@ -7784,7 +7540,7 @@ const getDisplayNameAssignmentIndex = (programRoot) => {
7784
7540
  const identifierTargets = /* @__PURE__ */ new Set();
7785
7541
  const memberPathSegments = /* @__PURE__ */ new Set();
7786
7542
  const visit = (node) => {
7787
- if (isNodeOfType(node, "AssignmentExpression") && isNodeOfType(node.left, "MemberExpression") && getStaticMemberName$2(node.left) === "displayName") {
7543
+ if (isNodeOfType(node, "AssignmentExpression") && isNodeOfType(node.left, "MemberExpression") && getStaticMemberName$1(node.left) === "displayName") {
7788
7544
  if (isNodeOfType(node.left.object, "Identifier")) identifierTargets.add(node.left.object.name);
7789
7545
  for (const segment of memberExpressionPath(node.left.object)) memberPathSegments.add(segment);
7790
7546
  }
@@ -7889,7 +7645,7 @@ const displayName = defineRule({
7889
7645
  }
7890
7646
  },
7891
7647
  CallExpression(node) {
7892
- if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall$1(node)) {
7648
+ if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall(node)) {
7893
7649
  const assignedName = getAssignedName(node);
7894
7650
  if (assignedName) {
7895
7651
  const programRoot = findProgramRoot(node);
@@ -7936,6 +7692,21 @@ const displayName = defineRule({
7936
7692
  }
7937
7693
  });
7938
7694
  //#endregion
7695
+ //#region src/plugin/utils/get-static-property-key-name.ts
7696
+ const getStaticPropertyKeyName = (node, options = {}) => {
7697
+ if (!isNodeOfType(node, "Property")) return null;
7698
+ if (node.computed) {
7699
+ if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
7700
+ return null;
7701
+ }
7702
+ if (isNodeOfType(node.key, "Identifier")) return node.key.name;
7703
+ if (isNodeOfType(node.key, "Literal")) {
7704
+ if (typeof node.key.value === "string") return node.key.value;
7705
+ if (options.stringifyNonStringLiterals) return String(node.key.value);
7706
+ }
7707
+ return null;
7708
+ };
7709
+ //#endregion
7939
7710
  //#region src/plugin/utils/read-static-boolean.ts
7940
7711
  const readStaticBoolean = (node) => {
7941
7712
  if (!node) return null;
@@ -7944,6 +7715,21 @@ const readStaticBoolean = (node) => {
7944
7715
  return unwrappedNode.value;
7945
7716
  };
7946
7717
  //#endregion
7718
+ //#region src/plugin/utils/resolve-const-identifier-alias.ts
7719
+ const resolveConstIdentifierAlias = (identifier, scopes) => {
7720
+ if (!isNodeOfType(identifier, "Identifier")) return null;
7721
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
7722
+ let symbol = scopes.symbolFor(identifier);
7723
+ while (symbol?.kind === "const") {
7724
+ if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
7725
+ visitedSymbolIds.add(symbol.id);
7726
+ const initializer = stripParenExpression(symbol.initializer);
7727
+ if (!isNodeOfType(initializer, "Identifier")) return symbol;
7728
+ symbol = scopes.symbolFor(initializer);
7729
+ }
7730
+ return symbol;
7731
+ };
7732
+ //#endregion
7947
7733
  //#region src/plugin/utils/is-react-api-call.ts
7948
7734
  const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
7949
7735
  const isImportedFromReact = (symbol) => {
@@ -7951,9 +7737,9 @@ const isImportedFromReact = (symbol) => {
7951
7737
  const importDeclaration = symbol.declarationNode.parent;
7952
7738
  return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(importDeclaration.source.value));
7953
7739
  };
7954
- const isNamedReactApiImport = (identifier, apiNames, scopes, resolveAliases) => {
7740
+ const isNamedReactApiImport = (identifier, apiNames, scopes) => {
7955
7741
  if (!isNodeOfType(identifier, "Identifier")) return false;
7956
- const symbol = resolveAliases ? resolveConstIdentifierAlias(identifier, scopes) : scopes.symbolFor(identifier);
7742
+ const symbol = scopes.symbolFor(identifier);
7957
7743
  if (!symbol || !isImportedFromReact(symbol)) return false;
7958
7744
  const importedName = getImportedName(symbol.declarationNode);
7959
7745
  return Boolean(importedName && includesApiName(apiNames, importedName));
@@ -7967,7 +7753,7 @@ const isReactApiCall = (node, apiNames, scopes, options = {}) => {
7967
7753
  if (!isNodeOfType(node, "CallExpression")) return false;
7968
7754
  const callee = stripParenExpression(node.callee);
7969
7755
  if (isNodeOfType(callee, "Identifier")) {
7970
- if (isNamedReactApiImport(callee, apiNames, scopes, Boolean(options.resolveNamedAliases))) return true;
7756
+ if (isNamedReactApiImport(callee, apiNames, scopes)) return true;
7971
7757
  return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
7972
7758
  }
7973
7759
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !includesApiName(apiNames, callee.property.name)) return false;
@@ -9070,30 +8856,6 @@ const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, conte
9070
8856
  }
9071
8857
  return matchingBlocks.size > 0;
9072
8858
  };
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
- };
9097
8859
  const removeSynchronouslyReleasedUsages = (callback, usages, context) => {
9098
8860
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return usages;
9099
8861
  if (!isNodeOfType(callback.body, "BlockStatement")) return usages;
@@ -9294,83 +9056,6 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
9294
9056
  });
9295
9057
  return didCleanupFunctionMatch;
9296
9058
  };
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);
9374
9059
  const effectHasCleanupForUsage = (callback, usage, context) => {
9375
9060
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
9376
9061
  if (usage.kind === "subscribe" && findEnclosingFunction(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
@@ -9383,10 +9068,6 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
9383
9068
  if (returnStart !== null && usageStart !== null && returnStart < usageStart) return;
9384
9069
  const returnedValue = child.argument ? stripParenExpression(child.argument) : null;
9385
9070
  if (!returnedValue) return;
9386
- if (doesBoundCleanupReleaseUsage(returnedValue, usage, context)) {
9387
- matchingCleanupReturns.push(child);
9388
- return;
9389
- }
9390
9071
  if (usage.kind === "subscribe" && (returnedValue === usage.node || getRangeStart(returnedValue) !== null && getRangeStart(returnedValue) === getRangeStart(usage.node)) && isCleanupReturningSubscribeLikeCallExpression(returnedValue)) {
9391
9072
  matchingCleanupReturns.push(child);
9392
9073
  return;
@@ -9402,17 +9083,13 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
9402
9083
  if (!context.scopes.symbolFor(returnedValue)?.initializer) return;
9403
9084
  }
9404
9085
  const cleanupFunction = resolveStableValue(returnedValue, context);
9405
- if (cleanupFunction && doesBoundCleanupReleaseUsage(cleanupFunction, usage, context)) {
9406
- matchingCleanupReturns.push(child);
9407
- return;
9408
- }
9409
9086
  if (!cleanupFunction || !isFunctionLike$1(cleanupFunction)) return;
9410
9087
  if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
9411
9088
  });
9412
9089
  return doMatchingNodesCoverEveryPathAfterUsage(resolveCleanupPathAnchor(usage.node, callback, context), matchingCleanupReturns, context);
9413
9090
  };
9414
9091
  const findFirstUsageWithoutCleanup = (callback, usages, context) => {
9415
- for (const usage of usages) if (!effectHasCleanupForUsage(callback, usage, context) && !hasSplitLifecycleCleanup(callback, usage, context)) return usage;
9092
+ for (const usage of usages) if (!effectHasCleanupForUsage(callback, usage, context)) return usage;
9416
9093
  return null;
9417
9094
  };
9418
9095
  const isLocalAbortControllerExpression = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
@@ -11285,11 +10962,6 @@ If the missing value is recreated every render, move it inside the hook or stabi
11285
10962
  return { CallExpression(node) {
11286
10963
  const hookName = getHookName(node.callee, context.scopes);
11287
10964
  if (!hookName || !isHookOfInterest(hookName, node.callee)) return;
11288
- if (HOOKS_REQUIRING_DEPS_MATCH.has(hookName) && !isReactApiCall(node, HOOKS_REQUIRING_DEPS_MATCH, context.scopes, {
11289
- allowGlobalReactNamespace: true,
11290
- allowUnboundBareCalls: true,
11291
- resolveNamedAliases: true
11292
- })) return;
11293
10965
  const callbackArgumentIndex = getCallbackArgumentIndex(hookName);
11294
10966
  const depsArgumentIndex = getDepsArgumentIndex(hookName);
11295
10967
  const callbackArgument = node.arguments[callbackArgumentIndex];
@@ -14584,11 +14256,7 @@ const jsIndexMaps = defineRule({
14584
14256
  //#region src/plugin/utils/are-expressions-structurally-equal.ts
14585
14257
  const areExpressionsStructurallyEqual = (a, b) => {
14586
14258
  if (!a || !b) return a === b;
14587
- const unwrappedA = stripParenExpression(a);
14588
- const unwrappedB = stripParenExpression(b);
14589
- if (unwrappedA !== a || unwrappedB !== b) return areExpressionsStructurallyEqual(unwrappedA, unwrappedB);
14590
14259
  if (a.type !== b.type) return false;
14591
- if (isNodeOfType(a, "ThisExpression")) return true;
14592
14260
  if (isNodeOfType(a, "Identifier") && isNodeOfType(b, "Identifier")) return a.name === b.name;
14593
14261
  if (isNodeOfType(a, "Literal") && isNodeOfType(b, "Literal")) return a.value === b.value;
14594
14262
  if (isNodeOfType(a, "MemberExpression") && isNodeOfType(b, "MemberExpression")) {
@@ -21131,8 +20799,8 @@ const catchClauseRethrowsCaught = (handler) => {
21131
20799
  return didRethrow;
21132
20800
  };
21133
20801
  //#endregion
21134
- //#region src/plugin/utils/is-immediately-invoked-function.ts
21135
- const isImmediatelyInvokedFunction = (functionNode) => {
20802
+ //#region src/plugin/utils/find-guarding-try-statement.ts
20803
+ const isImmediatelyInvokedFunctionCallee = (functionNode) => {
21136
20804
  let wrappedCallee = functionNode;
21137
20805
  let enclosing = functionNode.parent;
21138
20806
  while (enclosing && stripParenExpression(enclosing) === functionNode) {
@@ -21141,13 +20809,11 @@ const isImmediatelyInvokedFunction = (functionNode) => {
21141
20809
  }
21142
20810
  return Boolean(enclosing && isNodeOfType(enclosing, "CallExpression") && enclosing.callee === wrappedCallee);
21143
20811
  };
21144
- //#endregion
21145
- //#region src/plugin/utils/find-guarding-try-statement.ts
21146
20812
  const findGuardingTryStatement = (node) => {
21147
20813
  let child = node;
21148
20814
  let ancestor = node.parent;
21149
20815
  while (ancestor) {
21150
- if (isFunctionLike$1(ancestor) && !isImmediatelyInvokedFunction(ancestor)) return null;
20816
+ if (isFunctionLike$1(ancestor) && !isImmediatelyInvokedFunctionCallee(ancestor)) return null;
21151
20817
  if (isNodeOfType(ancestor, "TryStatement") && ancestor.block === child && ancestor.handler && !catchClauseRethrowsCaught(ancestor.handler)) return ancestor;
21152
20818
  child = ancestor;
21153
20819
  ancestor = ancestor.parent ?? null;
@@ -22280,8 +21946,6 @@ const DOM_QUERY_MEMBER_NAMES = new Set([
22280
21946
  ]);
22281
21947
  const LAYOUT_MEASUREMENT_MEMBER_NAMES = new Set([
22282
21948
  "current",
22283
- "textContent",
22284
- "innerText",
22285
21949
  "scrollWidth",
22286
21950
  "clientWidth",
22287
21951
  "offsetWidth",
@@ -22303,7 +21967,7 @@ const POST_MOUNT_GLOBAL_NAMES = new Set([
22303
21967
  "navigator"
22304
21968
  ]);
22305
21969
  const REF_FACTORY_CALLEE_NAMES = new Set(["useRef", "createRef"]);
22306
- const hasRefLikeName = (name) => name === "ref" || name.endsWith("Ref") || name.endsWith("ref") || name.endsWith("Node") || name.endsWith("node") || name.endsWith("Element") || name.endsWith("element");
21970
+ const hasRefLikeName = (name) => name === "ref" || name.endsWith("Ref") || name.endsWith("ref");
22307
21971
  const isRefFactoryInitializer = (init) => {
22308
21972
  if (!init || !isNodeOfType(init, "CallExpression")) return false;
22309
21973
  const callee = init.callee;
@@ -23217,73 +22881,61 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
23217
22881
  "Array",
23218
22882
  "BigInt",
23219
22883
  "Boolean",
23220
- "encodeURIComponent",
23221
22884
  "Number",
23222
22885
  "Object",
23223
22886
  "String",
23224
22887
  "parseFloat",
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"])]
22888
+ "parseInt"
23273
22889
  ]);
22890
+ const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
22891
+ const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
22892
+ "isRawJSON",
22893
+ "parse",
22894
+ "rawJSON",
22895
+ "stringify"
22896
+ ])], ["Math", new Set([
22897
+ "abs",
22898
+ "acos",
22899
+ "acosh",
22900
+ "asin",
22901
+ "asinh",
22902
+ "atan",
22903
+ "atan2",
22904
+ "atanh",
22905
+ "cbrt",
22906
+ "ceil",
22907
+ "clz32",
22908
+ "cos",
22909
+ "cosh",
22910
+ "exp",
22911
+ "floor",
22912
+ "fround",
22913
+ "hypot",
22914
+ "imul",
22915
+ "log",
22916
+ "log10",
22917
+ "log1p",
22918
+ "log2",
22919
+ "max",
22920
+ "min",
22921
+ "pow",
22922
+ "round",
22923
+ "sign",
22924
+ "sin",
22925
+ "sinh",
22926
+ "sqrt",
22927
+ "tan",
22928
+ "tanh",
22929
+ "trunc"
22930
+ ])]]);
23274
22931
  const PURE_MEMBER_TRANSFORM_NAMES = new Set([
23275
22932
  "concat",
23276
22933
  "filter",
23277
- "flatMap",
23278
22934
  "join",
23279
22935
  "map",
23280
- "reduce",
23281
- "replace",
23282
- "slice",
23283
22936
  "split",
23284
22937
  "toLowerCase",
23285
22938
  "toString",
23286
- "toSorted",
23287
22939
  "toUpperCase",
23288
22940
  "trim"
23289
22941
  ]);
@@ -23292,7 +22944,7 @@ const STORAGE_GLOBAL_NAMES$1 = new Set([
23292
22944
  "localStorage",
23293
22945
  "sessionStorage"
23294
22946
  ]);
23295
- const getStaticMemberName$1 = (node) => {
22947
+ const getStaticMemberName = (node) => {
23296
22948
  if (!isNodeOfType(node, "MemberExpression")) return null;
23297
22949
  if (!node.computed && isNodeOfType(node.property, "Identifier")) return node.property.name;
23298
22950
  if (node.computed && isNodeOfType(node.property, "Literal")) return typeof node.property.value === "string" ? node.property.value : null;
@@ -23307,7 +22959,7 @@ const getCallCalleeName$1 = (callExpression) => {
23307
22959
  if (!isNodeOfType(callExpression, "CallExpression")) return null;
23308
22960
  const callee = stripParenExpression(callExpression.callee);
23309
22961
  if (isNodeOfType(callee, "Identifier")) return callee.name;
23310
- return getStaticMemberName$1(callee);
22962
+ return getStaticMemberName(callee);
23311
22963
  };
23312
22964
  const getFunctionParameters = (functionNode) => isFunctionLike$1(functionNode) ? functionNode.params ?? [] : [];
23313
22965
  const getIdentifierBindingIdentity = (analysis, identifier) => {
@@ -23423,14 +23075,14 @@ const localUseEventPreservesCallback = (analysis, callee) => {
23423
23075
  if (!isNodeOfType(child, "CallExpression")) return;
23424
23076
  const forwardedCallee = stripParenExpression(child.callee);
23425
23077
  if (!isNodeOfType(forwardedCallee, "MemberExpression")) return;
23426
- if (getStaticMemberName$1(forwardedCallee) !== "current") return;
23078
+ if (getStaticMemberName(forwardedCallee) !== "current") return;
23427
23079
  if (!isNodeOfType(forwardedCallee.object, "Identifier")) return;
23428
23080
  const refReference = getRef(analysis, forwardedCallee.object);
23429
23081
  if (!callbackRefDeclarators.find((declarator) => refReference?.resolved?.defs.some((definition) => definition.node === declarator)) || !refReference?.resolved) return;
23430
23082
  if (!refReference.resolved.references.some((candidateReference) => {
23431
23083
  const memberExpression = candidateReference.identifier.parent;
23432
23084
  const assignmentExpression = memberExpression?.parent;
23433
- if (!memberExpression || !isNodeOfType(memberExpression, "MemberExpression") || getStaticMemberName$1(memberExpression) !== "current" || !assignmentExpression || !isNodeOfType(assignmentExpression, "AssignmentExpression") || assignmentExpression.left !== memberExpression) return false;
23085
+ if (!memberExpression || !isNodeOfType(memberExpression, "MemberExpression") || getStaticMemberName(memberExpression) !== "current" || !assignmentExpression || !isNodeOfType(assignmentExpression, "AssignmentExpression") || assignmentExpression.left !== memberExpression) return false;
23434
23086
  return getIdentifierBindingIdentity(analysis, assignmentExpression.right) !== callbackBinding;
23435
23087
  })) forwardsCallback = true;
23436
23088
  });
@@ -23455,7 +23107,7 @@ const resolveWrappedCallable = (analysis, node) => {
23455
23107
  return callback && isFunctionLike$1(callback) ? callback : null;
23456
23108
  }
23457
23109
  if (!isNodeOfType(candidate, "MemberExpression")) return null;
23458
- if (getStaticMemberName$1(candidate) !== "current") return null;
23110
+ if (getStaticMemberName(candidate) !== "current") return null;
23459
23111
  if (!isNodeOfType(candidate.object, "Identifier")) return null;
23460
23112
  const reference = getRef(analysis, candidate.object);
23461
23113
  const declarator = reference?.resolved?.defs.map((definition) => definition.node).find((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
@@ -23467,7 +23119,7 @@ const resolveWrappedCallable = (analysis, node) => {
23467
23119
  return Boolean(reference?.resolved?.references.some((candidateReference) => {
23468
23120
  const member = candidateReference.identifier.parent;
23469
23121
  const assignment = member?.parent;
23470
- return Boolean(member && isNodeOfType(member, "MemberExpression") && getStaticMemberName$1(member) === "current" && assignment && isNodeOfType(assignment, "AssignmentExpression") && assignment.left === member);
23122
+ return Boolean(member && isNodeOfType(member, "MemberExpression") && getStaticMemberName(member) === "current" && assignment && isNodeOfType(assignment, "AssignmentExpression") && assignment.left === member);
23471
23123
  })) ? null : initializer;
23472
23124
  };
23473
23125
  const functionInvokesItself = (analysis, functionNode) => {
@@ -23506,7 +23158,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
23506
23158
  if (!isNodeOfType(child, "CallExpression")) return;
23507
23159
  const callee = stripParenExpression(child.callee);
23508
23160
  const calleeName = getCallCalleeName$1(child);
23509
- const memberName = getStaticMemberName$1(callee);
23161
+ const memberName = getStaticMemberName(callee);
23510
23162
  const isDeferringCall = calleeName !== null && DEFERRING_CALLEE_NAMES.has(calleeName) || memberName !== null && DEFERRING_MEMBER_NAMES.has(memberName);
23511
23163
  const isIteratorCall = memberName !== null && SYNCHRONOUS_ITERATOR_METHOD_NAMES$1.has(memberName) && isNodeOfType(callee, "MemberExpression");
23512
23164
  const enqueueFrame = (callableNode, argumentsForCallable, isDeferred, introducedBindings, allowWithoutInvocationEvidence = false) => {
@@ -23665,19 +23317,14 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
23665
23317
  const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
23666
23318
  return callbackSummary.isValid && !callbackSummary.canContinue;
23667
23319
  }
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
- }
23673
23320
  if (isNodeOfType(node, "CallExpression")) {
23674
23321
  const callee = stripParenExpression(node.callee);
23675
23322
  const calleeRoot = getMemberRoot(callee);
23676
23323
  const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && !environment.parameterIndices.has(callee.name) && !environment.recursiveNames.has(callee.name) && !environment.shadowedGlobalNames.has(callee.name);
23677
23324
  const namespaceName = isNodeOfType(calleeRoot, "Identifier") && !environment.parameterIndices.has(calleeRoot.name) && !environment.recursiveNames.has(calleeRoot.name) && !environment.shadowedGlobalNames.has(calleeRoot.name) ? calleeRoot.name : null;
23678
- const namespaceMemberName = getStaticMemberName$1(callee);
23325
+ const namespaceMemberName = getStaticMemberName(callee);
23679
23326
  const isPureNamespaceCall = isNodeOfType(callee, "MemberExpression") && namespaceName !== null && namespaceMemberName !== null && PURE_HELPER_NAMESPACE_MEMBER_NAMES.get(namespaceName)?.has(namespaceMemberName) === true;
23680
- const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName$1(callee) ?? "");
23327
+ const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName(callee) ?? "");
23681
23328
  if (!isPureGlobalCall && !isPureNamespaceCall && !isPureMemberTransform) return false;
23682
23329
  if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression") && !analyzeHelperExpression(callee.object, environment, usedParameterIndices)) return false;
23683
23330
  return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
@@ -23751,7 +23398,7 @@ const isLocallyConstructedObjectMember = (reference, memberExpression) => isNode
23751
23398
  return isNodeOfType(definitionNode, "VariableDeclarator") && Boolean(definitionNode.init) && (isNodeOfType(definitionNode.init, "ObjectExpression") || isNodeOfType(definitionNode.init, "ArrayExpression"));
23752
23399
  }) === true;
23753
23400
  const getUseRefDeclarator = (analysis, memberExpression) => {
23754
- if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticMemberName$1(memberExpression) !== "current" || !isNodeOfType(memberExpression.object, "Identifier")) return null;
23401
+ if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticMemberName(memberExpression) !== "current" || !isNodeOfType(memberExpression.object, "Identifier")) return null;
23755
23402
  return getRef(analysis, memberExpression.object)?.resolved?.defs.map((definition) => definition.node).find((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator") && isNodeOfType(definitionNode.init, "CallExpression") && getCallCalleeName$1(definitionNode.init) === "useRef") ?? null;
23756
23403
  };
23757
23404
  const collectValueEvidence = (analysis, expression, frame, remainingCallFrames, visitedBindings = /* @__PURE__ */ new Set()) => {
@@ -23820,7 +23467,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23820
23467
  return collectValueEvidence(analysis, initializer.init, frame, remainingCallFrames, visitedBindings);
23821
23468
  }
23822
23469
  if (isNodeOfType(node, "MemberExpression")) {
23823
- if (getStaticMemberName$1(node) === "current" && isNodeOfType(node.object, "Identifier")) {
23470
+ if (getStaticMemberName(node) === "current" && isNodeOfType(node.object, "Identifier")) {
23824
23471
  const refBinding = getRef(analysis, node.object)?.resolved;
23825
23472
  const refDeclarator = useRefDeclarator;
23826
23473
  if (refBinding && refDeclarator && isNodeOfType(refDeclarator, "VariableDeclarator") && isNodeOfType(refDeclarator.init, "CallExpression")) {
@@ -23836,7 +23483,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23836
23483
  if (candidateReference.init) continue;
23837
23484
  const identifier = candidateReference.identifier;
23838
23485
  const member = identifier.parent;
23839
- if (!member || !isNodeOfType(member, "MemberExpression") || member.object !== identifier || getStaticMemberName$1(member) !== "current") {
23486
+ if (!member || !isNodeOfType(member, "MemberExpression") || member.object !== identifier || getStaticMemberName(member) !== "current") {
23840
23487
  evidence.hasUnknownSource = true;
23841
23488
  continue;
23842
23489
  }
@@ -23872,8 +23519,8 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23872
23519
  }
23873
23520
  const callee = stripParenExpression(node.callee);
23874
23521
  const calleeRoot = getMemberRoot(callee);
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;
23876
- const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName$1(callee) ?? "");
23522
+ const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(calleeRoot, "Identifier") && PURE_GLOBAL_NAMESPACE_NAMES.has(calleeRoot.name) && getIdentifierBindingIdentity(analysis, calleeRoot) === null;
23523
+ const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName(callee) ?? "");
23877
23524
  if (isPureGlobalCall || isPureMemberTransform) {
23878
23525
  if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
23879
23526
  for (const argument of node.arguments ?? []) {
@@ -23925,15 +23572,6 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23925
23572
  }
23926
23573
  return evidence;
23927
23574
  }
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
- }
23937
23575
  if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
23938
23576
  evidence.hasUnknownSource = true;
23939
23577
  return evidence;
@@ -24352,26 +23990,6 @@ const STATEFUL_HTML_DESCENDANT_TAGS = new Set([
24352
23990
  "dialog",
24353
23991
  "canvas"
24354
23992
  ]);
24355
- const STATEFUL_HTML_ATTRIBUTE_NAMES = new Set([
24356
- "autofocus",
24357
- "contenteditable",
24358
- "draggable",
24359
- "tabindex"
24360
- ]);
24361
- const isStaticallyFalseAttributeValue = (attribute) => {
24362
- if (!isNodeOfType(attribute, "JSXAttribute") || !attribute.value) return false;
24363
- const value = isNodeOfType(attribute.value, "JSXExpressionContainer") ? attribute.value.expression : attribute.value;
24364
- return isNodeOfType(value, "Literal") && (value.value === false || value.value === "false");
24365
- };
24366
- const hasStatefulHtmlAttribute = (openingElement) => {
24367
- if (!isNodeOfType(openingElement, "JSXOpeningElement")) return false;
24368
- return openingElement.attributes.some((attribute) => {
24369
- if (!isNodeOfType(attribute, "JSXAttribute") || !isNodeOfType(attribute.name, "JSXIdentifier")) return false;
24370
- const attributeName = attribute.name.name.toLowerCase();
24371
- if (!STATEFUL_HTML_ATTRIBUTE_NAMES.has(attributeName)) return false;
24372
- return attributeName === "tabindex" || !isStaticallyFalseAttributeValue(attribute);
24373
- });
24374
- };
24375
23993
  const STATEFUL_DESCENDANT_SCAN_BUDGET = 200;
24376
23994
  const rootIdentifierNameOf = (expression) => {
24377
23995
  let object = expression;
@@ -24389,8 +24007,7 @@ const containsStatefulDescendant = (jsxElement, options = {}) => {
24389
24007
  budget -= 1;
24390
24008
  const node = stack.pop();
24391
24009
  if (isNodeOfType(node, "JSXElement")) {
24392
- const opening = node.openingElement;
24393
- const name = opening.name;
24010
+ const name = node.openingElement.name;
24394
24011
  if (name && isNodeOfType(name, "JSXIdentifier")) {
24395
24012
  const tagName = name.name;
24396
24013
  const firstChar = tagName.charCodeAt(0);
@@ -24398,7 +24015,6 @@ const containsStatefulDescendant = (jsxElement, options = {}) => {
24398
24015
  if (STATEFUL_HTML_DESCENDANT_TAGS.has(tagName)) return true;
24399
24016
  }
24400
24017
  if (name && isNodeOfType(name, "JSXMemberExpression")) return true;
24401
- if (hasStatefulHtmlAttribute(opening)) return true;
24402
24018
  const children = node.children ?? [];
24403
24019
  for (const child of children) stack.push(child);
24404
24020
  continue;
@@ -24958,14 +24574,6 @@ const templateHasOuterMemberIdentity = (template, bindingFunction) => {
24958
24574
  }
24959
24575
  return false;
24960
24576
  };
24961
- const findBareItemNamesReferencedByTemplate = (template, itemNames) => {
24962
- const referencedItemNames = /* @__PURE__ */ new Set();
24963
- for (const expression of template.expressions ?? []) {
24964
- const unwrappedExpression = stripParenExpression(expression);
24965
- if (isNodeOfType(unwrappedExpression, "Identifier") && itemNames.has(unwrappedExpression.name)) referencedItemNames.add(unwrappedExpression.name);
24966
- }
24967
- return referencedItemNames;
24968
- };
24969
24577
  const forLoopTestReadsDataLength = (test) => {
24970
24578
  let didFindLengthRead = false;
24971
24579
  walkAst(test, (child) => {
@@ -25102,11 +24710,9 @@ const noArrayIndexAsKey = defineRule({
25102
24710
  } else if (STATELESS_HTML_LEAF_TAGS.has(elementName.name)) {
25103
24711
  const jsxElement = openingElement.parent;
25104
24712
  if (jsxElement && isNodeOfType(jsxElement, "JSXElement")) {
25105
- const isInlineTextRun = INLINE_TEXT_LEAF_TAGS.has(elementName.name);
25106
- const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET;
25107
24713
  if (!containsStatefulDescendant(jsxElement, {
25108
- memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET,
25109
- bareIdentifierNames: primitiveItemNames.size > 0 ? new Set([...derivedNames, ...primitiveItemNames]) : derivedNames
24714
+ memberRootNames: INLINE_TEXT_LEAF_TAGS.has(elementName.name) ? itemNames : EMPTY_NAME_SET,
24715
+ bareIdentifierNames: derivedNames
25110
24716
  })) return;
25111
24717
  }
25112
24718
  }
@@ -25274,11 +24880,7 @@ const noAsyncEffectCallback = defineRule({
25274
24880
  severity: "warn",
25275
24881
  recommendation: "Don't make the effect callback `async`. Define an async function inside the effect and call it, then return a real cleanup function if you need one.",
25276
24882
  create: (context) => ({ CallExpression(node) {
25277
- if (!isReactApiCall(node, EFFECT_HOOK_NAMES$1, context.scopes, {
25278
- allowGlobalReactNamespace: true,
25279
- allowUnboundBareCalls: true,
25280
- resolveNamedAliases: true
25281
- })) return;
24883
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
25282
24884
  const callback = getEffectCallback(node);
25283
24885
  if (!callback) return;
25284
24886
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
@@ -26476,19 +26078,6 @@ const noCloneElement = defineRule({
26476
26078
  } })
26477
26079
  });
26478
26080
  //#endregion
26479
- //#region src/plugin/utils/get-call-method-name.ts
26480
- /**
26481
- * Returns the static method name of a call's callee when it's a
26482
- * non-computed MemberExpression (`obj.method` → `"method"`), or
26483
- * `null` otherwise. Used by `no-pass-data-to-parent` and
26484
- * `no-pass-live-state-to-parent` to match the receiver-bound
26485
- * method-call shape against an allow/block list.
26486
- */
26487
- const getCallMethodName = (callee) => {
26488
- if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
26489
- return null;
26490
- };
26491
- //#endregion
26492
26081
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
26493
26082
  const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
26494
26083
  const CONTEXT_MODULES = [
@@ -26496,35 +26085,23 @@ const CONTEXT_MODULES = [
26496
26085
  "use-context-selector",
26497
26086
  "react-tracked"
26498
26087
  ];
26499
- const getSupportedContextImportSource = (symbol) => {
26500
- if (symbol?.kind !== "import") return null;
26501
- const importDeclaration = symbol.declarationNode.parent;
26502
- if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string" || !CONTEXT_MODULES.includes(importDeclaration.source.value)) return null;
26503
- return importDeclaration.source.value;
26504
- };
26505
- const isSupportedNamespaceSymbol = (symbol) => getSupportedContextImportSource(symbol) !== null && Boolean(symbol && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
26506
- const isDestructuredCreateContextBinding = (identifier, scopes) => {
26507
- if (!isNodeOfType(identifier, "Identifier")) return false;
26508
- const symbol = scopes.symbolFor(identifier);
26509
- if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "ObjectPattern")) return false;
26510
- const property = symbol.bindingIdentifier.parent;
26511
- if (!property || !isNodeOfType(property, "Property") || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "createContext") return false;
26512
- const initializer = stripParenExpression(symbol.initializer);
26513
- return isNodeOfType(initializer, "Identifier") && isSupportedNamespaceSymbol(resolveConstIdentifierAlias(initializer, scopes));
26514
- };
26515
- const isCreateContextCall = (node, scopes) => {
26516
- const callee = stripParenExpression(node.callee);
26088
+ const isCreateContextCallee = (callee) => {
26517
26089
  if (isNodeOfType(callee, "Identifier")) {
26518
- if (isDestructuredCreateContextBinding(callee, scopes)) return true;
26519
- const symbol = resolveConstIdentifierAlias(callee, scopes);
26520
- return getSupportedContextImportSource(symbol) !== null && Boolean(symbol && getImportedName(symbol.declarationNode) === "createContext");
26090
+ const binding = getImportBindingForName(callee, callee.name);
26091
+ return binding !== null && binding.exportedName === "createContext" && CONTEXT_MODULES.includes(binding.source);
26521
26092
  }
26522
- if (!isNodeOfType(callee, "MemberExpression")) return false;
26523
- if ((getCallMethodName(callee) ?? (callee.computed && isNodeOfType(callee.property, "Literal") && typeof callee.property.value === "string" ? callee.property.value : null)) !== "createContext") return false;
26524
- const receiver = stripParenExpression(callee.object);
26525
- if (!isNodeOfType(receiver, "Identifier")) return false;
26526
- if (receiver.name === "React" && scopes.isGlobalReference(receiver)) return true;
26527
- return isSupportedNamespaceSymbol(resolveConstIdentifierAlias(receiver, scopes));
26093
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed) {
26094
+ const namespaceIdentifier = callee.object;
26095
+ const propertyIdentifier = callee.property;
26096
+ if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
26097
+ if (!isNodeOfType(propertyIdentifier, "Identifier")) return false;
26098
+ if (propertyIdentifier.name !== "createContext") return false;
26099
+ const namespaceName = namespaceIdentifier.name;
26100
+ if (isCanonicalReactNamespaceName(namespaceName)) return true;
26101
+ const importSource = getImportSourceForName(namespaceIdentifier, namespaceName);
26102
+ return importSource !== null && CONTEXT_MODULES.includes(importSource);
26103
+ }
26104
+ return false;
26528
26105
  };
26529
26106
  const noCreateContextInRender = defineRule({
26530
26107
  id: "no-create-context-in-render",
@@ -26533,7 +26110,7 @@ const noCreateContextInRender = defineRule({
26533
26110
  category: "Correctness",
26534
26111
  recommendation: "Move `createContext(...)` outside the component, to the top level of the file, so it stays the same on every render.",
26535
26112
  create: (context) => ({ CallExpression(node) {
26536
- if (!isCreateContextCall(node, context.scopes)) return;
26113
+ if (!isCreateContextCallee(node.callee)) return;
26537
26114
  const componentOrHookName = enclosingComponentOrHookName(node);
26538
26115
  if (!componentOrHookName) return;
26539
26116
  context.report({
@@ -27768,7 +27345,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
27768
27345
  let ancestor = setStateCall.parent;
27769
27346
  while (ancestor) {
27770
27347
  if (!lifecycleMember) {
27771
- if (FUNCTION_NODE_TYPES$1.has(ancestor.type) && (!isImmediatelyInvokedFunction(ancestor) || !("async" in ancestor) || ancestor.async === true)) nestedFunctionCount += 1;
27348
+ if (FUNCTION_NODE_TYPES$1.has(ancestor.type)) nestedFunctionCount += 1;
27772
27349
  if (isLifecycleMember(ancestor, lifecycleNames)) lifecycleMember = ancestor;
27773
27350
  } else if (isEs5Component(ancestor) || isEs6Component(ancestor)) {
27774
27351
  if (nestedFunctionCount > 1 && !options.disallowInNestedFunctions) return false;
@@ -27970,7 +27547,7 @@ const collectDiffSourceLocalNames = (lifecycleFunction, paramNames) => {
27970
27547
  const body = lifecycleFunction.body;
27971
27548
  if (!body) return derivedNames;
27972
27549
  walkAst(body, (node) => {
27973
- if (FUNCTION_NODE_TYPES.has(node.type) && !isImmediatelyInvokedFunction(node)) return false;
27550
+ if (FUNCTION_NODE_TYPES.has(node.type)) return false;
27974
27551
  if (!isNodeOfType(node, "VariableDeclarator")) return;
27975
27552
  const init = node.init;
27976
27553
  if (!init) return;
@@ -27980,67 +27557,6 @@ const collectDiffSourceLocalNames = (lifecycleFunction, paramNames) => {
27980
27557
  return derivedNames;
27981
27558
  };
27982
27559
  const isStatefulOperand = (node, paramNames, derivedNames) => referencesAnyName(node, paramNames) || referencesAnyName(node, derivedNames) || containsThisStateOrProps(node);
27983
- const getStaticMemberName = (node) => {
27984
- if (!isNodeOfType(node, "MemberExpression") || node.computed === true) return null;
27985
- return isNodeOfType(node.property, "Identifier") ? node.property.name : null;
27986
- };
27987
- const getThisStateFieldName = (node) => {
27988
- const unwrappedNode = stripParenExpression(node);
27989
- if (!isNodeOfType(unwrappedNode, "MemberExpression")) return null;
27990
- const object = stripParenExpression(unwrappedNode.object);
27991
- if (!isNodeOfType(object, "MemberExpression") || !isNodeOfType(stripParenExpression(object.object), "ThisExpression") || getStaticMemberName(object) !== "state") return null;
27992
- return getStaticMemberName(unwrappedNode);
27993
- };
27994
- const collectLocalInitializers = (lifecycleFunction) => {
27995
- const initializers = /* @__PURE__ */ new Map();
27996
- const body = lifecycleFunction.body;
27997
- if (!body) return initializers;
27998
- walkAst(body, (node) => {
27999
- if (FUNCTION_NODE_TYPES.has(node.type) && !isImmediatelyInvokedFunction(node)) return false;
28000
- if (isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && node.init) initializers.set(node.id.name, node.init);
28001
- });
28002
- return initializers;
28003
- };
28004
- const derivesFromPostMountValue = (node, localInitializers, visitedNames = /* @__PURE__ */ new Set()) => {
28005
- if (readsPostMountValue(node)) return true;
28006
- const referencedNames = /* @__PURE__ */ new Set();
28007
- collectReferenceIdentifierNames(node, referencedNames);
28008
- for (const referencedName of referencedNames) {
28009
- if (visitedNames.has(referencedName)) continue;
28010
- const initializer = localInitializers.get(referencedName);
28011
- if (!initializer) continue;
28012
- if (derivesFromPostMountValue(initializer, localInitializers, new Set([...visitedNames, referencedName]))) return true;
28013
- }
28014
- return false;
28015
- };
28016
- const getSetStateFieldValue = (setStateCall, fieldName) => {
28017
- if (!isNodeOfType(setStateCall, "CallExpression")) return null;
28018
- const argument = setStateCall.arguments?.[0];
28019
- if (!argument || !isNodeOfType(argument, "ObjectExpression")) return null;
28020
- for (const property of argument.properties ?? []) {
28021
- if (!isNodeOfType(property, "Property") || property.computed === true) continue;
28022
- if ((isNodeOfType(property.key, "Identifier") && property.key.name || isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" && property.key.value || null) === fieldName) return property.value;
28023
- }
28024
- return null;
28025
- };
28026
- const isConvergentPostMountGuard = (test, setStateCall, localInitializers) => {
28027
- let qualifies = false;
28028
- walkAst(test, (node) => {
28029
- if (qualifies) return false;
28030
- if (!isNodeOfType(node, "BinaryExpression") || !EQUALITY_OPERATORS.has(node.operator)) return;
28031
- const leftFieldName = getThisStateFieldName(node.left);
28032
- const rightFieldName = getThisStateFieldName(node.right);
28033
- const fieldName = leftFieldName ?? rightFieldName;
28034
- const comparedValue = leftFieldName ? node.right : node.left;
28035
- if (!fieldName || !leftFieldName && !rightFieldName) return;
28036
- const assignedValue = getSetStateFieldValue(setStateCall, fieldName);
28037
- if (!assignedValue || !areExpressionsStructurallyEqual(comparedValue, assignedValue)) return;
28038
- if (!derivesFromPostMountValue(comparedValue, localInitializers)) return;
28039
- qualifies = true;
28040
- return false;
28041
- });
28042
- return qualifies;
28043
- };
28044
27560
  const isDiffGuardTest = (test, paramNames, derivedNames) => {
28045
27561
  if (referencesAnyName(test, paramNames)) return true;
28046
27562
  let qualifies = false;
@@ -28048,7 +27564,7 @@ const isDiffGuardTest = (test, paramNames, derivedNames) => {
28048
27564
  if (qualifies) return false;
28049
27565
  if (!isNodeOfType(node, "BinaryExpression")) return;
28050
27566
  if (!EQUALITY_OPERATORS.has(node.operator)) return;
28051
- if (isStatefulOperand(node.left, paramNames, derivedNames) && isStatefulOperand(node.right, paramNames, derivedNames) && (referencesAnyName(node.left, derivedNames) || referencesAnyName(node.right, derivedNames))) {
27567
+ if (isStatefulOperand(node.left, paramNames, derivedNames) && isStatefulOperand(node.right, paramNames, derivedNames)) {
28052
27568
  qualifies = true;
28053
27569
  return false;
28054
27570
  }
@@ -28061,12 +27577,11 @@ const isInsideDiffGuard = (setStateCall) => {
28061
27577
  const paramNames = /* @__PURE__ */ new Set();
28062
27578
  for (const param of lifecycleFunction.params ?? []) collectPatternNames(param, paramNames);
28063
27579
  const derivedNames = collectDiffSourceLocalNames(lifecycleFunction, paramNames);
28064
- const localInitializers = collectLocalInitializers(lifecycleFunction);
28065
27580
  let child = setStateCall;
28066
27581
  let ancestor = setStateCall.parent;
28067
27582
  while (ancestor && ancestor !== lifecycleFunction) {
28068
27583
  const guardTest = isNodeOfType(ancestor, "IfStatement") && child !== ancestor.test && ancestor.test || isNodeOfType(ancestor, "ConditionalExpression") && child !== ancestor.test && ancestor.test || isNodeOfType(ancestor, "LogicalExpression") && ancestor.operator === "&&" && child === ancestor.right && ancestor.left || null;
28069
- if (guardTest && (isDiffGuardTest(guardTest, paramNames, derivedNames) || isConvergentPostMountGuard(guardTest, setStateCall, localInitializers))) return true;
27584
+ if (guardTest && isDiffGuardTest(guardTest, paramNames, derivedNames)) return true;
28070
27585
  child = ancestor;
28071
27586
  ancestor = ancestor.parent ?? null;
28072
27587
  }
@@ -28483,31 +27998,20 @@ const noDocumentStartViewTransition = defineRule({
28483
27998
  } })
28484
27999
  });
28485
28000
  //#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
28495
28001
  //#region src/plugin/rules/js-performance/no-document-write.ts
28496
28002
  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.";
28497
28003
  const WRITE_METHODS = new Set(["write", "writeln"]);
28498
- const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
28499
28004
  const noDocumentWrite = defineRule({
28500
28005
  id: "no-document-write",
28501
28006
  title: "document.write/writeln",
28502
28007
  severity: "warn",
28503
28008
  recommendation: "Don't use `document.write()`/`document.writeln()`. Append DOM nodes or set `innerHTML`/`textContent` on a specific element instead.",
28504
28009
  create: (context) => ({ CallExpression(node) {
28505
- const callee = stripParenExpression(node.callee);
28506
- if (!isNodeOfType(callee, "MemberExpression")) return;
28010
+ const callee = node.callee;
28011
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return;
28507
28012
  const receiver = stripParenExpression(callee.object);
28508
- if (!isNodeOfType(receiver, "Identifier") || !isGlobalDocumentReference(receiver, context)) return;
28509
- const methodName = getStaticPropertyName(callee);
28510
- if (methodName === null || !WRITE_METHODS.has(methodName)) return;
28013
+ if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "document") return;
28014
+ if (!isNodeOfType(callee.property, "Identifier") || !WRITE_METHODS.has(callee.property.name)) return;
28511
28015
  context.report({
28512
28016
  node,
28513
28017
  message: MESSAGE$24
@@ -29314,14 +28818,6 @@ const noEffectWithFreshDeps = defineRule({
29314
28818
  //#endregion
29315
28819
  //#region src/plugin/rules/security/no-eval.ts
29316
28820
  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
- };
29325
28821
  const noEval = defineRule({
29326
28822
  id: "no-eval",
29327
28823
  title: "eval() runs untrusted code strings",
@@ -29331,21 +28827,20 @@ const noEval = defineRule({
29331
28827
  if (SANDBOX_SURFACE_PATH_PATTERN.test(normalizeFilename(context.filename ?? ""))) return {};
29332
28828
  return {
29333
28829
  CallExpression(node) {
29334
- const executableGlobalName = getExecutableGlobalName(node.callee, context);
29335
- if (executableGlobalName === "eval") {
28830
+ if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "eval") {
29336
28831
  context.report({
29337
28832
  node,
29338
28833
  message: "eval() is a code-injection vulnerability: it runs any string as code."
29339
28834
  });
29340
28835
  return;
29341
28836
  }
29342
- if ((executableGlobalName === "setTimeout" || executableGlobalName === "setInterval") && isNodeOfType(node.arguments?.[0], "Literal") && typeof node.arguments[0].value === "string") context.report({
28837
+ if (isNodeOfType(node.callee, "Identifier") && (node.callee.name === "setTimeout" || node.callee.name === "setInterval") && isNodeOfType(node.arguments?.[0], "Literal") && typeof node.arguments[0].value === "string") context.report({
29343
28838
  node,
29344
- message: `Passing a string to ${executableGlobalName}() is a code-injection vulnerability, since it runs that string as code.`
28839
+ message: `Passing a string to ${node.callee.name}() is a code-injection vulnerability, since it runs that string as code.`
29345
28840
  });
29346
28841
  },
29347
28842
  NewExpression(node) {
29348
- if (getExecutableGlobalName(node.callee, context) === "Function") {
28843
+ if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "Function") {
29349
28844
  const onlyArgument = node.arguments.length === 1 ? node.arguments[0] : void 0;
29350
28845
  if (isNodeOfType(onlyArgument, "Literal") && typeof onlyArgument.value === "string" && onlyArgument.value.trim() === "return this") return;
29351
28846
  context.report({
@@ -30604,11 +30099,7 @@ const noFetchInEffect = defineRule({
30604
30099
  severity: "warn",
30605
30100
  recommendation: "Use a data-fetching layer or Server Component so fetches do not race, double-fire, or leak from `useEffect`.",
30606
30101
  create: (context) => ({ CallExpression(node) {
30607
- if (!isReactApiCall(node, EFFECT_HOOK_NAMES$1, context.scopes, {
30608
- allowGlobalReactNamespace: true,
30609
- allowUnboundBareCalls: true,
30610
- resolveNamedAliases: true
30611
- })) return;
30102
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
30612
30103
  const callback = getEffectCallback(node);
30613
30104
  if (!callback) return;
30614
30105
  const analysisFunctions = collectEffectAnalysisFunctions(callback, context);
@@ -34818,6 +34309,19 @@ const DATA_SINK_METHOD_NAMES = new Set([
34818
34309
  "deserialize"
34819
34310
  ]);
34820
34311
  //#endregion
34312
+ //#region src/plugin/utils/get-call-method-name.ts
34313
+ /**
34314
+ * Returns the static method name of a call's callee when it's a
34315
+ * non-computed MemberExpression (`obj.method` → `"method"`), or
34316
+ * `null` otherwise. Used by `no-pass-data-to-parent` and
34317
+ * `no-pass-live-state-to-parent` to match the receiver-bound
34318
+ * method-call shape against an allow/block list.
34319
+ */
34320
+ const getCallMethodName = (callee) => {
34321
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
34322
+ return null;
34323
+ };
34324
+ //#endregion
34821
34325
  //#region src/plugin/rules/state-and-effects/no-pass-data-to-parent.ts
34822
34326
  const isUseStateIdentifier = (identifier) => {
34823
34327
  if (!isNodeOfType(identifier, "Identifier")) return false;
@@ -35062,14 +34566,6 @@ const isHandlerBagArgument = (analysis, argument) => {
35062
34566
  };
35063
34567
  const getFunctionalUpdaterDataRefs = (analysis, updater) => getDownstreamRefs(analysis, updater).filter((updaterRef) => !updaterRef.resolved?.defs.some((def) => def.type === "Parameter" && def.node === updater));
35064
34568
  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
- ]);
35073
34569
  const isParentWiredHookResultRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
35074
34570
  const node = def.node;
35075
34571
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
@@ -35092,19 +34588,6 @@ const isParentWiredHookCalleeRef = (analysis, ref) => {
35092
34588
  if (!parent || !isNodeOfType(parent, "CallExpression") || parent.callee !== identifier) return false;
35093
34589
  return (parent.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isProp(analysis, downstreamRef)));
35094
34590
  };
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
- };
35108
34591
  const isImportBindingRef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "ImportBinding"));
35109
34592
  const isCalleePosition = (identifier) => {
35110
34593
  const parent = identifier.parent;
@@ -35166,11 +34649,10 @@ const noPassDataToParent = defineRule({
35166
34649
  if (argumentRef && resolveToFunction(argumentRef)) return [];
35167
34650
  }
35168
34651
  return getDownstreamRefs(analysis, argument);
35169
- }).flatMap((argumentRef) => isExternallyDrivenState(analysis, argumentRef) ? [] : getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
34652
+ }).flatMap((argumentRef) => getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
35170
34653
  if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
35171
34654
  if (!argsUpstreamRefs.some((argRef) => {
35172
34655
  if (isUseStateIdentifier(argRef.identifier)) return false;
35173
- if (isExternalSubscriptionHookRef(argRef)) return false;
35174
34656
  if (isProp(analysis, argRef)) return false;
35175
34657
  if (isUseRefIdentifier(argRef.identifier)) return false;
35176
34658
  if (isRefCurrent(argRef)) return false;
@@ -35465,30 +34947,6 @@ const guardsRenderShape = (comparison) => {
35465
34947
  }
35466
34948
  return false;
35467
34949
  };
35468
- const isPropsChildrenLength = (node, scopes) => {
35469
- const unwrappedNode = stripParenExpression(node);
35470
- return isNodeOfType(unwrappedNode, "MemberExpression") && !unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Identifier") && unwrappedNode.property.name === "length" && resolvesToPropsChildren(stripParenExpression(unwrappedNode.object), scopes);
35471
- };
35472
- const isLargeTextLengthComparison = (node, scopes) => {
35473
- const unwrappedNode = stripParenExpression(node);
35474
- if (!isNodeOfType(unwrappedNode, "BinaryExpression")) return false;
35475
- const leftIsLength = isPropsChildrenLength(unwrappedNode.left, scopes);
35476
- const rightIsLength = isPropsChildrenLength(unwrappedNode.right, scopes);
35477
- const thresholdNode = leftIsLength ? unwrappedNode.right : unwrappedNode.left;
35478
- if (!leftIsLength && !rightIsLength || !isNodeOfType(thresholdNode, "Literal")) return false;
35479
- if (typeof thresholdNode.value !== "number" || thresholdNode.value < 1e3) return false;
35480
- return leftIsLength ? unwrappedNode.operator === ">" || unwrappedNode.operator === ">=" : unwrappedNode.operator === "<" || unwrappedNode.operator === "<=";
35481
- };
35482
- const isLargeStringOptimizationGuard = (comparison, scopes) => {
35483
- let current = findTransparentExpressionRoot(comparison);
35484
- while (current.parent) {
35485
- const parent = current.parent;
35486
- if (!isNodeOfType(parent, "LogicalExpression") || parent.operator !== "&&") return false;
35487
- if (isLargeTextLengthComparison(parent.left === current ? parent.right : parent.left, scopes)) return true;
35488
- current = findTransparentExpressionRoot(parent);
35489
- }
35490
- return false;
35491
- };
35492
34950
  const noPolymorphicChildren = defineRule({
35493
34951
  id: "no-polymorphic-children",
35494
34952
  title: "Children type checked at runtime",
@@ -35502,7 +34960,6 @@ const noPolymorphicChildren = defineRule({
35502
34960
  const isStringLiteral = (operand) => isNodeOfType(operand, "Literal") && operand.value === "string";
35503
34961
  if (!isStringLiteral(node.left) && !isStringLiteral(node.right)) return;
35504
34962
  if (!guardsRenderShape(node)) return;
35505
- if (isLargeStringOptimizationGuard(node, context.scopes)) return;
35506
34963
  context.report({
35507
34964
  node,
35508
34965
  message: "Your users hit inconsistent behavior because `typeof children === \"string\"` makes this component switch on what callers pass, so add clear subcomponents like `<Button.Text>` instead."
@@ -35720,27 +35177,6 @@ const hasPreviousValueDep = (effectNode, depElements) => {
35720
35177
  }
35721
35178
  return false;
35722
35179
  };
35723
- const isStateLikeDependency = (analysis, element, isPropName) => {
35724
- if (!isNodeOfType(element, "Identifier") || isPropName(element.name)) return false;
35725
- if (!analysis) return true;
35726
- const reference = getRef(analysis, element);
35727
- if (!reference) return true;
35728
- const upstreamReferences = getUpstreamRefs(analysis, reference);
35729
- if (upstreamReferences.some((upstreamReference) => isState(analysis, upstreamReference))) return true;
35730
- return !upstreamReferences.some((upstreamReference) => isProp(analysis, upstreamReference));
35731
- };
35732
- const getRefHeldPropCallbackName = (callExpression, isPropName) => {
35733
- const callee = stripParenExpression(callExpression.callee);
35734
- if (!isNodeOfType(callee, "MemberExpression") || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "current") return null;
35735
- const receiver = stripParenExpression(callee.object);
35736
- if (!isNodeOfType(receiver, "Identifier")) return null;
35737
- const binding = findVariableInitializer(callExpression, receiver.name);
35738
- if (!binding?.initializer || !isNodeOfType(binding.initializer, "CallExpression")) return null;
35739
- if (getCalleeName$2(binding.initializer) !== "useRef") return null;
35740
- const callbackArgument = binding.initializer.arguments?.[0];
35741
- if (!callbackArgument || !isNodeOfType(callbackArgument, "Identifier")) return null;
35742
- return isPropName(callbackArgument.name) ? callbackArgument.name : null;
35743
- };
35744
35180
  const noPropCallbackInEffect = defineRule({
35745
35181
  id: "no-prop-callback-in-effect",
35746
35182
  title: "Parent kept in sync with a callback effect",
@@ -35757,11 +35193,11 @@ const noPropCallbackInEffect = defineRule({
35757
35193
  if (!callback || !isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
35758
35194
  const depsNode = node.arguments[1];
35759
35195
  if (!isNodeOfType(depsNode, "ArrayExpression") || !depsNode.elements?.length) return;
35760
- const analysis = getProgramAnalysis(node);
35761
- const stateLikeDeps = (depsNode.elements ?? []).filter((element) => element && isStateLikeDependency(analysis, element, propStackTracker.isPropName));
35196
+ const stateLikeDeps = (depsNode.elements ?? []).filter((element) => isNodeOfType(element, "Identifier") && !propStackTracker.isPropName(element.name));
35762
35197
  if (stateLikeDeps.length === 0) return;
35763
35198
  if (isRefLatchGuardedEffect(callback.body)) return;
35764
35199
  if (hasPreviousValueDep(node, depsNode.elements ?? [])) return;
35200
+ const analysis = getProgramAnalysis(node);
35765
35201
  if (analysis) {
35766
35202
  const stateLikeDepRefs = [];
35767
35203
  for (const element of stateLikeDeps) {
@@ -35773,9 +35209,9 @@ const noPropCallbackInEffect = defineRule({
35773
35209
  const reportedNodes = /* @__PURE__ */ new Set();
35774
35210
  walkInsideStatementBlocks(callback.body, (child) => {
35775
35211
  if (!isNodeOfType(child, "CallExpression")) return;
35776
- const directCallee = stripParenExpression(child.callee);
35777
- const calleeName = isNodeOfType(directCallee, "Identifier") && propStackTracker.isPropName(directCallee.name) && directCallee.name || getRefHeldPropCallbackName(child, propStackTracker.isPropName);
35778
- if (!calleeName) return;
35212
+ if (!isNodeOfType(child.callee, "Identifier")) return;
35213
+ const calleeName = child.callee.name;
35214
+ if (!propStackTracker.isPropName(calleeName)) return;
35779
35215
  if (!isResultDiscardedCall(child)) return;
35780
35216
  if (reportedNodes.has(child)) return;
35781
35217
  reportedNodes.add(child);
@@ -43616,6 +43052,15 @@ const isStableHookWrapperArgument = (node) => {
43616
43052
  const parent = node.parent;
43617
43053
  return isNodeOfType(parent, "CallExpression") && isNodeOfType(parent.callee, "Identifier") && STABLE_HOOK_WRAPPERS.has(parent.callee.name) && Boolean(parent.arguments?.some((argument) => argument === node));
43618
43054
  };
43055
+ const isImmediatelyInvokedFunction = (functionNode) => {
43056
+ let wrappedCallee = functionNode;
43057
+ let enclosing = functionNode.parent;
43058
+ while (enclosing && stripParenExpression(enclosing) === functionNode) {
43059
+ wrappedCallee = enclosing;
43060
+ enclosing = enclosing.parent ?? null;
43061
+ }
43062
+ return Boolean(enclosing && isNodeOfType(enclosing, "CallExpression") && enclosing.callee === wrappedCallee);
43063
+ };
43619
43064
  const queryStableQueryClient = defineRule({
43620
43065
  id: "query-stable-query-client",
43621
43066
  title: "Unstable QueryClient in component",
@@ -54928,6 +54373,12 @@ const webhookSignatureRisk = defineRule({
54928
54373
  //#region src/plugin/rules/zod/utils/zod-ast.ts
54929
54374
  const ZOD_MODULE = "zod";
54930
54375
  const ZOD_MODULE_SOURCES = [ZOD_MODULE];
54376
+ const getStaticPropertyName = (member) => {
54377
+ const property = member.property;
54378
+ if (!member.computed && isNodeOfType(property, "Identifier")) return property.name;
54379
+ if (member.computed && isNodeOfType(property, "Literal") && typeof property.value === "string") return property.value;
54380
+ return null;
54381
+ };
54931
54382
  const importInfoCache = /* @__PURE__ */ new WeakMap();
54932
54383
  const getImportInfoForIdentifier = (identifier) => {
54933
54384
  if (importInfoCache.has(identifier)) return importInfoCache.get(identifier) ?? null;