oxlint-plugin-react-doctor 0.7.7-dev.43327b5 → 0.7.7-dev.4fce1c1

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 (3) hide show
  1. package/dist/index.d.ts +0 -46
  2. package/dist/index.js +437 -1140
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,13 +7,6 @@ import { analyze } from "eslint-scope";
7
7
  //#region src/plugin/utils/is-node-of-type.ts
8
8
  const isNodeOfType = (node, type) => node !== null && typeof node === "object" && "type" in node && node.type === type;
9
9
  //#endregion
10
- //#region src/plugin/utils/is-type-only-import.ts
11
- const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
12
- if (!specifiers || specifiers.length === 0) return false;
13
- return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
14
- };
15
- const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
16
- //#endregion
17
10
  //#region src/plugin/utils/non-react-jsx-dialect.ts
18
11
  const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
19
12
  "solid-js",
@@ -28,33 +21,17 @@ const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
28
21
  "vidode"
29
22
  ]);
30
23
  const NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES = ["solid-js", "@builder.io/qwik"];
31
- const REACT_JSX_DIALECT_PACKAGE_PREFIXES = [
32
- "react",
33
- "react-dom",
34
- "preact"
35
- ];
36
24
  const startsWithAny = (source, prefixes) => prefixes.some((prefix) => source === prefix || source.startsWith(`${prefix}/`));
37
- const collectJsxRuntimeImports = (program) => {
38
- let hasNonReactRuntime = false;
39
- let hasReactRuntime = false;
25
+ const fileImportsNonReactJsxDialect = (program) => {
40
26
  for (const statement of program.body) {
41
27
  if (!isNodeOfType(statement, "ImportDeclaration")) continue;
42
- const importDeclaration = statement;
43
- if (isTypeOnlyImport(importDeclaration)) continue;
44
- const source = importDeclaration.source;
28
+ const source = statement.source;
45
29
  const value = source && typeof source.value === "string" ? source.value : null;
46
30
  if (!value) continue;
47
- if (NON_REACT_JSX_DIALECT_PACKAGES.has(value) || startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) hasNonReactRuntime = true;
48
- if (startsWithAny(value, REACT_JSX_DIALECT_PACKAGE_PREFIXES)) hasReactRuntime = true;
31
+ if (NON_REACT_JSX_DIALECT_PACKAGES.has(value)) return true;
32
+ if (startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) return true;
49
33
  }
50
- return {
51
- hasNonReactRuntime,
52
- hasReactRuntime
53
- };
54
- };
55
- const fileImportsNonReactJsxDialect = (program) => {
56
- const runtimeImports = collectJsxRuntimeImports(program);
57
- return runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
34
+ return false;
58
35
  };
59
36
  const jsxAttributeIsNonReactDialectMarker = (openingNode) => {
60
37
  for (const attribute of openingNode.attributes) {
@@ -267,7 +244,6 @@ const VISITOR_NODE_NAME_PATTERN = /^[A-Z]/;
267
244
  const wrapCreateForReactJsxOnly = (create) => ((context) => {
268
245
  const innerVisitors = create(context);
269
246
  let fileIsNonReactJsx = false;
270
- let fileImportsReactRuntime = false;
271
247
  const wrappedVisitors = {};
272
248
  for (const [key, visitor] of Object.entries(innerVisitors)) {
273
249
  if (typeof visitor !== "function") {
@@ -280,16 +256,14 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
280
256
  }
281
257
  if (key === "Program") {
282
258
  wrappedVisitors.Program = (node) => {
283
- const runtimeImports = collectJsxRuntimeImports(node);
284
- fileImportsReactRuntime = runtimeImports.hasReactRuntime;
285
- fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
259
+ fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
286
260
  visitor(node);
287
261
  };
288
262
  continue;
289
263
  }
290
264
  if (key === "JSXOpeningElement") {
291
265
  wrappedVisitors.JSXOpeningElement = (node) => {
292
- if (!fileImportsReactRuntime && !fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
266
+ if (!fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
293
267
  if (fileIsNonReactJsx) return;
294
268
  visitor(node);
295
269
  };
@@ -301,9 +275,7 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
301
275
  };
302
276
  }
303
277
  if (!("Program" in wrappedVisitors)) wrappedVisitors.Program = (node) => {
304
- const runtimeImports = collectJsxRuntimeImports(node);
305
- fileImportsReactRuntime = runtimeImports.hasReactRuntime;
306
- fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
278
+ fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
307
279
  };
308
280
  return wrappedVisitors;
309
281
  });
@@ -2340,7 +2312,7 @@ const anchorAmbiguousText = defineRule({
2340
2312
  });
2341
2313
  //#endregion
2342
2314
  //#region src/plugin/rules/a11y/anchor-has-content.ts
2343
- const MESSAGE$62 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2315
+ const MESSAGE$61 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2344
2316
  const isTransComponentsTemplate = (node) => {
2345
2317
  let current = node.parent;
2346
2318
  while (current) {
@@ -2376,7 +2348,7 @@ const anchorHasContent = defineRule({
2376
2348
  if (isTransComponentsTemplate(node)) return;
2377
2349
  context.report({
2378
2350
  node: opening.name,
2379
- message: MESSAGE$62
2351
+ message: MESSAGE$61
2380
2352
  });
2381
2353
  } };
2382
2354
  }
@@ -2808,7 +2780,7 @@ const parseJsxValue = (value) => {
2808
2780
  };
2809
2781
  //#endregion
2810
2782
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
2811
- const MESSAGE$61 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2783
+ const MESSAGE$60 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2812
2784
  const mayBeContentEditable = (node) => {
2813
2785
  const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
2814
2786
  if (!attribute) return false;
@@ -2839,7 +2811,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2839
2811
  if (tabIndexValue === null || tabIndexValue >= -1) return;
2840
2812
  context.report({
2841
2813
  node: node.name,
2842
- message: MESSAGE$61
2814
+ message: MESSAGE$60
2843
2815
  });
2844
2816
  return;
2845
2817
  }
@@ -2847,7 +2819,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2847
2819
  if (mayBeContentEditable(node)) return;
2848
2820
  context.report({
2849
2821
  node: node.name,
2850
- message: MESSAGE$61
2822
+ message: MESSAGE$60
2851
2823
  });
2852
2824
  } })
2853
2825
  });
@@ -5252,7 +5224,7 @@ const asyncParallel = defineRule({
5252
5224
  });
5253
5225
  //#endregion
5254
5226
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
5255
- const MESSAGE$60 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
5227
+ const MESSAGE$59 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
5256
5228
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
5257
5229
  const STORAGE_GLOBALS = new Set([
5258
5230
  "window",
@@ -5317,7 +5289,7 @@ const authTokenInWebStorage = defineRule({
5317
5289
  if (keyString === null || !isAuthCredentialKey(keyString)) return;
5318
5290
  context.report({
5319
5291
  node,
5320
- message: MESSAGE$60
5292
+ message: MESSAGE$59
5321
5293
  });
5322
5294
  },
5323
5295
  AssignmentExpression(node) {
@@ -5328,7 +5300,7 @@ const authTokenInWebStorage = defineRule({
5328
5300
  if (!propertyName || !isAuthCredentialKey(propertyName)) return;
5329
5301
  context.report({
5330
5302
  node: target,
5331
- message: MESSAGE$60
5303
+ message: MESSAGE$59
5332
5304
  });
5333
5305
  }
5334
5306
  }))
@@ -5467,7 +5439,7 @@ const CI_INSTALL_NEAR_SECRET_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b
5467
5439
  const INSTALL_COMMAND_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b/i;
5468
5440
  const SECRET_REFERENCE_PATTERN = /\bsecrets\.[A-Z0-9_]+/;
5469
5441
  const IGNORE_SCRIPTS_FLAG_PATTERN = /--ignore-scripts\b/;
5470
- const MESSAGE$59 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5442
+ const MESSAGE$58 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5471
5443
  const isWorkflowPath = (relativePath) => /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath);
5472
5444
  const scanWorkflowContent = (content) => {
5473
5445
  const lines = content.split("\n");
@@ -5512,7 +5484,7 @@ const scanWorkflowContent = (content) => {
5512
5484
  const installLineOffset = Math.max(step.lines.findIndex((stepLine) => INSTALL_COMMAND_PATTERN.test(stepLine)), 0);
5513
5485
  const installColumnIndex = step.lines[installLineOffset].search(INSTALL_COMMAND_PATTERN);
5514
5486
  return [{
5515
- message: MESSAGE$59,
5487
+ message: MESSAGE$58,
5516
5488
  line: step.startLineIndex + installLineOffset + 1,
5517
5489
  column: (installColumnIndex === -1 ? 0 : installColumnIndex) + 1
5518
5490
  }];
@@ -5522,7 +5494,7 @@ const scanWorkflowContent = (content) => {
5522
5494
  const scanNonWorkflowConfig = scanByPattern({
5523
5495
  shouldScan: (file) => isConfigOrCiPath(file.relativePath) && !file.relativePath.endsWith("package.json") && !isWorkflowPath(file.relativePath),
5524
5496
  pattern: CI_INSTALL_NEAR_SECRET_PATTERN,
5525
- message: MESSAGE$59
5497
+ message: MESSAGE$58
5526
5498
  });
5527
5499
  const scan = (file) => {
5528
5500
  if (isWorkflowPath(file.relativePath)) return scanWorkflowContent(file.content);
@@ -6019,17 +5991,15 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
6019
5991
  //#endregion
6020
5992
  //#region src/plugin/utils/get-static-property-key-name.ts
6021
5993
  const getStaticPropertyKeyName = (node, options = {}) => {
6022
- if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
6023
- const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
5994
+ if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition")) return null;
6024
5995
  if (node.computed) {
6025
- if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
6026
- if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
5996
+ if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
6027
5997
  return null;
6028
5998
  }
6029
- if (isNodeOfType(key, "Identifier")) return key.name;
6030
- if (isNodeOfType(key, "Literal")) {
6031
- if (typeof key.value === "string") return key.value;
6032
- if (options.stringifyNonStringLiterals) return String(key.value);
5999
+ if (isNodeOfType(node.key, "Identifier")) return node.key.name;
6000
+ if (isNodeOfType(node.key, "Literal")) {
6001
+ if (typeof node.key.value === "string") return node.key.value;
6002
+ if (options.stringifyNonStringLiterals) return String(node.key.value);
6033
6003
  }
6034
6004
  return null;
6035
6005
  };
@@ -6291,7 +6261,7 @@ const isPureEventBlockerHandler = (attribute) => {
6291
6261
  };
6292
6262
  //#endregion
6293
6263
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
6294
- const MESSAGE$58 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6264
+ const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6295
6265
  const KEY_HANDLERS = [
6296
6266
  "onKeyUp",
6297
6267
  "onKeyDown",
@@ -6502,7 +6472,7 @@ const clickEventsHaveKeyEvents = defineRule({
6502
6472
  if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6503
6473
  context.report({
6504
6474
  node: node.name,
6505
- message: MESSAGE$58
6475
+ message: MESSAGE$57
6506
6476
  });
6507
6477
  } };
6508
6478
  }
@@ -8755,7 +8725,7 @@ const getClassNameLiteral = (classAttribute) => {
8755
8725
  };
8756
8726
  //#endregion
8757
8727
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
8758
- const MESSAGE$57 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
8728
+ const MESSAGE$56 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
8759
8729
  const NON_OPERABLE_ELEMENTS = new Set([
8760
8730
  "td",
8761
8731
  "th",
@@ -8873,7 +8843,6 @@ const HTML_FOR_ATTRIBUTE = "htmlFor";
8873
8843
  const LABEL_ELEMENT = "label";
8874
8844
  const LABEL_COMPONENT_NAME = "Label";
8875
8845
  const POLYMORPHIC_COMPONENT_PROP = "component";
8876
- const TITLE_ATTRIBUTE = "title";
8877
8846
  const WRAPPER_LABEL_PROP = "label";
8878
8847
  const SELECT_ELEMENT = "select";
8879
8848
  const DEFAULT_DEPTH = 5;
@@ -8919,96 +8888,6 @@ const hasNonEmptyPropValue = (attribute) => {
8919
8888
  }
8920
8889
  return true;
8921
8890
  };
8922
- const getLastJsxPropIgnoreCase = (attributes, targetProp) => {
8923
- const targetPropLower = targetProp.toLowerCase();
8924
- for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
8925
- const attribute = attributes[attributeIndex];
8926
- if (!attribute || !isNodeOfType(attribute, "JSXAttribute")) continue;
8927
- if (getJsxAttributeName(attribute.name)?.toLowerCase() === targetPropLower) return attribute;
8928
- }
8929
- };
8930
- const getStaticNativeTitleArrayValue = (expression, scopes) => {
8931
- const elementValues = [];
8932
- for (const rawElement of expression.elements) {
8933
- if (rawElement === null) {
8934
- elementValues.push("");
8935
- continue;
8936
- }
8937
- if (isNodeOfType(rawElement, "SpreadElement")) return null;
8938
- const element = stripParenExpression(rawElement);
8939
- if (isNodeOfType(element, "Literal")) {
8940
- elementValues.push(element.value === null ? "" : String(element.value));
8941
- continue;
8942
- }
8943
- if (isNodeOfType(element, "TemplateLiteral")) {
8944
- const staticValue = getStaticTemplateLiteralValue(element);
8945
- if (staticValue === null) return null;
8946
- elementValues.push(staticValue);
8947
- continue;
8948
- }
8949
- if (isNodeOfType(element, "Identifier") && element.name === "undefined" && scopes.isGlobalReference(element) || isNodeOfType(element, "UnaryExpression") && element.operator === "void") {
8950
- elementValues.push("");
8951
- continue;
8952
- }
8953
- if (isNodeOfType(element, "ArrayExpression")) {
8954
- const nestedValue = getStaticNativeTitleArrayValue(element, scopes);
8955
- if (nestedValue === null) return null;
8956
- elementValues.push(nestedValue);
8957
- continue;
8958
- }
8959
- return null;
8960
- }
8961
- return elementValues.join(",");
8962
- };
8963
- const isGlobalSymbolExpression = (expression, scopes) => {
8964
- if (isNodeOfType(expression, "Identifier")) return expression.name === "Symbol" && scopes.isGlobalReference(expression);
8965
- if (!isNodeOfType(expression, "MemberExpression")) return false;
8966
- const object = stripParenExpression(expression.object);
8967
- return isNodeOfType(object, "Identifier") && object.name === "Symbol" && scopes.isGlobalReference(object);
8968
- };
8969
- const hasNonEmptyNativeTitleExpression = (rawExpression, scopes) => {
8970
- const expression = stripParenExpression(rawExpression);
8971
- if (isNodeOfType(expression, "Literal")) {
8972
- if (typeof expression.value === "string") return expression.value.trim().length > 0;
8973
- return expression.value !== null && typeof expression.value !== "boolean";
8974
- }
8975
- if (isNodeOfType(expression, "TemplateLiteral")) {
8976
- const staticValue = getStaticTemplateLiteralValue(expression);
8977
- return staticValue === null || staticValue.trim().length > 0;
8978
- }
8979
- if (isNodeOfType(expression, "Identifier")) return expression.name !== "undefined" || !scopes.isGlobalReference(expression);
8980
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return false;
8981
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return false;
8982
- if (isNodeOfType(expression, "ArrowFunctionExpression")) return false;
8983
- if (isNodeOfType(expression, "FunctionExpression")) return false;
8984
- if (isNodeOfType(expression, "ClassExpression")) return false;
8985
- if (isNodeOfType(expression, "ArrayExpression")) {
8986
- const staticValue = getStaticNativeTitleArrayValue(expression, scopes);
8987
- return staticValue === null || staticValue.trim().length > 0;
8988
- }
8989
- if (isGlobalSymbolExpression(expression, scopes)) return false;
8990
- if (isNodeOfType(expression, "CallExpression") && isGlobalSymbolExpression(expression.callee, scopes)) return false;
8991
- if (isNodeOfType(expression, "ConditionalExpression")) return hasNonEmptyNativeTitleExpression(expression.consequent, scopes) && hasNonEmptyNativeTitleExpression(expression.alternate, scopes);
8992
- if (isNodeOfType(expression, "SequenceExpression")) {
8993
- const finalExpression = expression.expressions.at(-1);
8994
- return finalExpression ? hasNonEmptyNativeTitleExpression(finalExpression, scopes) : false;
8995
- }
8996
- if (isNodeOfType(expression, "LogicalExpression")) {
8997
- const leftExpression = stripParenExpression(expression.left);
8998
- if (!isNodeOfType(leftExpression, "Literal")) return expression.operator !== "&&";
8999
- if (expression.operator === "??") return hasNonEmptyNativeTitleExpression(leftExpression.value === null ? expression.right : leftExpression, scopes);
9000
- const leftValueIsTruthy = Boolean(leftExpression.value);
9001
- if (expression.operator === "&&") return hasNonEmptyNativeTitleExpression(leftValueIsTruthy ? expression.right : leftExpression, scopes);
9002
- return hasNonEmptyNativeTitleExpression(leftValueIsTruthy ? leftExpression : expression.right, scopes);
9003
- }
9004
- return true;
9005
- };
9006
- const hasNonEmptyNativeTitle = (attribute, scopes) => {
9007
- if (!attribute?.value) return false;
9008
- if (isNodeOfType(attribute.value, "Literal")) return typeof attribute.value.value === "string" && attribute.value.value.trim().length > 0;
9009
- if (isNodeOfType(attribute.value, "JSXExpressionContainer")) return hasNonEmptyNativeTitleExpression(attribute.value.expression, scopes);
9010
- return true;
9011
- };
9012
8891
  const toAttributeMatchKey = (kind, value) => {
9013
8892
  const trimmedValue = value.trim();
9014
8893
  return trimmedValue.length > 0 ? `${kind}:${trimmedValue}` : null;
@@ -9265,7 +9144,6 @@ const controlHasAssociatedLabel = defineRule({
9265
9144
  if (typeValue === "submit" || typeValue === "reset") return;
9266
9145
  if (typeValue === "button" && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "value"))) return;
9267
9146
  }
9268
- if (isDomElement && hasNonEmptyNativeTitle(getLastJsxPropIgnoreCase(opening.attributes, TITLE_ATTRIBUTE), context.scopes)) return;
9269
9147
  if (supportsPlaceholderNameFallback(tagName, opening) && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "placeholder"))) return;
9270
9148
  if (hasLabellingProp(opening.attributes, settings.labelAttributes)) return;
9271
9149
  if (isInsideJsxAttribute(node)) return;
@@ -9285,7 +9163,7 @@ const controlHasAssociatedLabel = defineRule({
9285
9163
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
9286
9164
  context.report({
9287
9165
  node: candidate.opening,
9288
- message: MESSAGE$57
9166
+ message: MESSAGE$56
9289
9167
  });
9290
9168
  }
9291
9169
  }
@@ -10046,7 +9924,7 @@ const noVagueButtonLabel = defineRule({
10046
9924
  });
10047
9925
  //#endregion
10048
9926
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
10049
- const MESSAGE$56 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
9927
+ const MESSAGE$55 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
10050
9928
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
10051
9929
  const NAME_PROVIDING_ATTRIBUTES = [
10052
9930
  "aria-label",
@@ -10071,7 +9949,7 @@ const dialogHasAccessibleName = defineRule({
10071
9949
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
10072
9950
  context.report({
10073
9951
  node: node.name,
10074
- message: MESSAGE$56
9952
+ message: MESSAGE$55
10075
9953
  });
10076
9954
  } };
10077
9955
  }
@@ -10111,7 +9989,7 @@ const isEs6Component = (node) => {
10111
9989
  };
10112
9990
  //#endregion
10113
9991
  //#region src/plugin/rules/react-builtins/display-name.ts
10114
- const MESSAGE$55 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
9992
+ const MESSAGE$54 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
10115
9993
  const DEFAULT_ADDITIONAL_HOCS = [
10116
9994
  "observer",
10117
9995
  "lazy",
@@ -10304,7 +10182,7 @@ const displayName = defineRule({
10304
10182
  const reportAt = (node) => {
10305
10183
  context.report({
10306
10184
  node,
10307
- message: MESSAGE$55
10185
+ message: MESSAGE$54
10308
10186
  });
10309
10187
  };
10310
10188
  return {
@@ -11125,14 +11003,6 @@ const PROMISE_CHAIN_METHOD_NAMES$1 = new Set([
11125
11003
  "catch",
11126
11004
  "finally"
11127
11005
  ]);
11128
- const isPromiseChainCall = (callee) => isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && PROMISE_CHAIN_METHOD_NAMES$1.has(callee.property.name) && isNodeOfType(stripParenExpression(callee.object), "CallExpression");
11129
- const getPromiseChainCallForCallback = (candidate) => {
11130
- let callbackContainer = candidate.parent;
11131
- while (callbackContainer && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(callbackContainer.type)) callbackContainer = callbackContainer.parent;
11132
- if (!isNodeOfType(callbackContainer, "CallExpression")) return null;
11133
- if (!callbackContainer.arguments?.some((argument) => stripParenExpression(argument) === candidate)) return null;
11134
- return isPromiseChainCall(stripParenExpression(callbackContainer.callee)) ? callbackContainer : null;
11135
- };
11136
11006
  const collectEffectInvokedFunctions = (effectCallback) => {
11137
11007
  const invokedFunctions = new Set([effectCallback]);
11138
11008
  const localFunctionBindings = /* @__PURE__ */ new Map();
@@ -11144,6 +11014,7 @@ const collectEffectInvokedFunctions = (effectCallback) => {
11144
11014
  invokedFunctions.add(strippedCandidate);
11145
11015
  pendingFunctions.push(strippedCandidate);
11146
11016
  };
11017
+ const isPromiseChainCall = (callee) => isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && PROMISE_CHAIN_METHOD_NAMES$1.has(callee.property.name) && isNodeOfType(stripParenExpression(callee.object), "CallExpression");
11147
11018
  while (pendingFunctions.length > 0) {
11148
11019
  const currentFunction = pendingFunctions.pop();
11149
11020
  if (!currentFunction) break;
@@ -11351,50 +11222,6 @@ const isCleanupReturningSubscribeLikeCallExpression = (node) => {
11351
11222
  return true;
11352
11223
  };
11353
11224
  //#endregion
11354
- //#region src/plugin/utils/is-node-reachable-within-function.ts
11355
- const isInsideStaticallyUnreachableBranch = (node) => {
11356
- let child = node;
11357
- let parent = node.parent;
11358
- while (parent) {
11359
- if (isNodeOfType(parent, "IfStatement") && isNodeOfType(parent.test, "Literal")) {
11360
- if (parent.test.value === false && parent.consequent === child) return true;
11361
- if (parent.test.value === true && parent.alternate === child) return true;
11362
- }
11363
- if (isNodeOfType(parent, "ConditionalExpression") && isNodeOfType(parent.test, "Literal")) {
11364
- if (parent.test.value === false && parent.consequent === child) return true;
11365
- if (parent.test.value === true && parent.alternate === child) return true;
11366
- }
11367
- if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) {
11368
- if (isNodeOfType(parent.left, "Literal") && (parent.operator === "&&" && !parent.left.value || parent.operator === "||" && Boolean(parent.left.value))) return true;
11369
- }
11370
- child = parent;
11371
- parent = parent.parent;
11372
- }
11373
- return false;
11374
- };
11375
- const isNodeReachableWithinFunction = (node, context) => {
11376
- if (isInsideStaticallyUnreachableBranch(node)) return false;
11377
- const owner = context.cfg.enclosingFunction(node);
11378
- if (!owner) return true;
11379
- const functionCfg = context.cfg.cfgFor(owner);
11380
- if (!functionCfg) return true;
11381
- const targetBlock = functionCfg.blockOf(node);
11382
- if (!targetBlock) return true;
11383
- const visitedBlocks = new Set([functionCfg.entry]);
11384
- const pendingBlocks = [functionCfg.entry];
11385
- while (pendingBlocks.length > 0) {
11386
- const currentBlock = pendingBlocks.pop();
11387
- if (!currentBlock) break;
11388
- if (currentBlock === targetBlock) return true;
11389
- for (const edge of currentBlock.successors) {
11390
- if (visitedBlocks.has(edge.to)) continue;
11391
- visitedBlocks.add(edge.to);
11392
- pendingBlocks.push(edge.to);
11393
- }
11394
- }
11395
- return false;
11396
- };
11397
- //#endregion
11398
11225
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
11399
11226
  const OBSERVER_REGISTRATION_METHOD_NAME = "observe";
11400
11227
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
@@ -11523,6 +11350,27 @@ const findSubscribeLikeUsages = (callback, context) => {
11523
11350
  });
11524
11351
  return usages.filter((usage) => isNodeReachableWithinFunction(usage.node, context));
11525
11352
  };
11353
+ const isNodeReachableWithinFunction = (node, context) => {
11354
+ const owner = context.cfg.enclosingFunction(node);
11355
+ if (!owner) return true;
11356
+ const functionCfg = context.cfg.cfgFor(owner);
11357
+ if (!functionCfg) return true;
11358
+ const targetBlock = functionCfg.blockOf(node);
11359
+ if (!targetBlock) return true;
11360
+ const visitedBlocks = new Set([functionCfg.entry]);
11361
+ const pendingBlocks = [functionCfg.entry];
11362
+ while (pendingBlocks.length > 0) {
11363
+ const currentBlock = pendingBlocks.pop();
11364
+ if (!currentBlock) break;
11365
+ if (currentBlock === targetBlock) return true;
11366
+ for (const edge of currentBlock.successors) {
11367
+ if (visitedBlocks.has(edge.to)) continue;
11368
+ visitedBlocks.add(edge.to);
11369
+ pendingBlocks.push(edge.to);
11370
+ }
11371
+ }
11372
+ return false;
11373
+ };
11526
11374
  const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, context) => {
11527
11375
  let pathAnchor = usageNode;
11528
11376
  let pathOwner = findEnclosingFunction(pathAnchor);
@@ -11996,94 +11844,6 @@ const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
11996
11844
  return didFindUnmountCleanup;
11997
11845
  };
11998
11846
  const hasSplitLifecycleCleanup = (callback, usage, context) => usage.handleKey !== null && hasRerunReleaseBeforeUsage(callback, usage, context) && hasStableUnmountCleanupForUsage(callback, usage, context);
11999
- const collectBlockingBooleanStates = (expression, blockedExpressionValue, context) => {
12000
- const unwrappedExpression = stripParenExpression(expression);
12001
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return collectBlockingBooleanStates(unwrappedExpression.argument, !blockedExpressionValue, context);
12002
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
12003
- if (!(unwrappedExpression.operator === "||" && blockedExpressionValue || unwrappedExpression.operator === "&&" && !blockedExpressionValue)) return [];
12004
- return [...collectBlockingBooleanStates(unwrappedExpression.left, blockedExpressionValue, context), ...collectBlockingBooleanStates(unwrappedExpression.right, blockedExpressionValue, context)];
12005
- }
12006
- if (isNodeOfType(unwrappedExpression, "BinaryExpression") && [
12007
- "===",
12008
- "==",
12009
- "!==",
12010
- "!="
12011
- ].includes(unwrappedExpression.operator)) {
12012
- const leftValue = readStaticBoolean(unwrappedExpression.left);
12013
- const rightValue = readStaticBoolean(unwrappedExpression.right);
12014
- const booleanValue = leftValue ?? rightValue;
12015
- const comparedKey = resolveExpressionKey(leftValue === null ? unwrappedExpression.left : unwrappedExpression.right, context);
12016
- if (booleanValue === null || comparedKey === null) return [];
12017
- return [{
12018
- key: comparedKey,
12019
- value: (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "==") === blockedExpressionValue ? booleanValue : !booleanValue
12020
- }];
12021
- }
12022
- const expressionKey = resolveExpressionKey(unwrappedExpression, context);
12023
- return expressionKey === null ? [] : [{
12024
- key: expressionKey,
12025
- value: blockedExpressionValue
12026
- }];
12027
- };
12028
- const isDirectEarlyReturnConsequent = (ifStatement) => {
12029
- if (!isNodeOfType(ifStatement, "IfStatement") || ifStatement.alternate) return false;
12030
- if (isNodeOfType(ifStatement.consequent, "ReturnStatement")) return true;
12031
- return isNodeOfType(ifStatement.consequent, "BlockStatement") && ifStatement.consequent.body.length === 1 && isNodeOfType(ifStatement.consequent.body[0], "ReturnStatement");
12032
- };
12033
- const collectDeferredUsageGuardStates = (callback, usageNode, context) => {
12034
- if (!isFunctionLike$2(callback) || callback.async) return [];
12035
- const guardStates = [];
12036
- walkAst(callback.body, (child) => {
12037
- if (child !== callback.body && isFunctionLike$2(child)) return false;
12038
- if (isNodeOfType(child, "IfStatement") && isDirectEarlyReturnConsequent(child) && doMatchingNodesCoverEveryPathBeforeUsage(usageNode, [child], callback, context)) guardStates.push(...collectBlockingBooleanStates(child.test, true, context));
12039
- });
12040
- let descendant = usageNode;
12041
- let ancestor = descendant.parent;
12042
- while (ancestor && ancestor !== callback) {
12043
- if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant) guardStates.push(...collectBlockingBooleanStates(ancestor.test, false, context));
12044
- descendant = ancestor;
12045
- ancestor = ancestor.parent;
12046
- }
12047
- return guardStates;
12048
- };
12049
- const cleanupReturnInvalidatesGuard = (cleanupReturn, guardState, context) => {
12050
- if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return false;
12051
- const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
12052
- if (!cleanupFunction || !isFunctionLike$2(cleanupFunction) || cleanupFunction.async) return false;
12053
- let didInvalidateGuard = false;
12054
- walkAst(cleanupFunction.body, (child) => {
12055
- if (didInvalidateGuard) return false;
12056
- if (child !== cleanupFunction.body && isFunctionLike$2(child)) return false;
12057
- if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveExpressionKey(child.left, context) === guardState.key && readStaticBoolean(child.right) === guardState.value && context.cfg.isUnconditionalFromEntry(child)) {
12058
- didInvalidateGuard = true;
12059
- return false;
12060
- }
12061
- });
12062
- return didInvalidateGuard;
12063
- };
12064
- const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, context) => {
12065
- const usageStart = getRangeStart(usageNode);
12066
- if (!isFunctionLike$2(callback) || usageStart === null) return true;
12067
- let didWriteGuard = false;
12068
- walkAst(callback.body, (child) => {
12069
- if (didWriteGuard) return false;
12070
- if (child !== callback.body && isFunctionLike$2(child)) return false;
12071
- const childStart = getRangeStart(child);
12072
- if (childStart === null || childStart >= usageStart) return;
12073
- const writtenExpression = isNodeOfType(child, "AssignmentExpression") ? child.left : isNodeOfType(child, "UpdateExpression") ? child.argument : null;
12074
- if (writtenExpression && resolveExpressionKey(writtenExpression, context) === guardState.key) {
12075
- didWriteGuard = true;
12076
- return false;
12077
- }
12078
- });
12079
- return didWriteGuard;
12080
- };
12081
- const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
12082
- const usageFunction = findEnclosingFunction(usage.node);
12083
- const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
12084
- if (usage.handleKey === null || !usageFunction || usageFunction === callback || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
12085
- return collectDeferredUsageGuardStates(usageFunction, usage.node, context).some((guardState) => !deferredUsageWritesGuardBeforeUsage(usageFunction, usage.node, guardState, context) && cleanupReturns.every((cleanupReturn) => cleanupReturnInvalidatesGuard(cleanupReturn, guardState, context)));
12086
- };
12087
11847
  const effectHasCleanupForUsage = (callback, usage, context) => {
12088
11848
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
12089
11849
  if (callback.async) return false;
@@ -12123,7 +11883,6 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
12123
11883
  if (!cleanupFunction || !isFunctionLike$2(cleanupFunction)) return;
12124
11884
  if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
12125
11885
  });
12126
- if (hasGuardedDeferredCleanup(callback, usage, matchingCleanupReturns, context)) return true;
12127
11886
  return doMatchingNodesCoverEveryPathAfterUsage(resolveCleanupPathAnchor(usage.node, callback, context), matchingCleanupReturns, context);
12128
11887
  };
12129
11888
  const findFirstUsageWithoutCleanup = (callback, usages, context) => {
@@ -14867,7 +14626,7 @@ const forbidElements = defineRule({
14867
14626
  });
14868
14627
  //#endregion
14869
14628
  //#region src/plugin/rules/react-builtins/forward-ref-uses-ref.ts
14870
- const MESSAGE$54 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
14629
+ const MESSAGE$53 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
14871
14630
  const forwardRefUsesRef = defineRule({
14872
14631
  id: "forward-ref-uses-ref",
14873
14632
  title: "forwardRef without ref parameter",
@@ -14890,7 +14649,7 @@ const forwardRefUsesRef = defineRule({
14890
14649
  if (isNodeOfType(onlyParam, "RestElement")) return;
14891
14650
  context.report({
14892
14651
  node: inner,
14893
- message: MESSAGE$54
14652
+ message: MESSAGE$53
14894
14653
  });
14895
14654
  } })
14896
14655
  });
@@ -14931,7 +14690,7 @@ const gitProviderUrlInjectionRisk = defineRule({
14931
14690
  });
14932
14691
  //#endregion
14933
14692
  //#region src/plugin/rules/a11y/heading-has-content.ts
14934
- const MESSAGE$53 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
14693
+ const MESSAGE$52 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
14935
14694
  const DEFAULT_HEADING_TAGS = [
14936
14695
  "h1",
14937
14696
  "h2",
@@ -14965,7 +14724,7 @@ const headingHasContent = defineRule({
14965
14724
  for (const attribute of ["aria-label", "aria-labelledby"]) if (hasJsxPropIgnoreCase(node.attributes, attribute)) return;
14966
14725
  context.report({
14967
14726
  node,
14968
- message: MESSAGE$53
14727
+ message: MESSAGE$52
14969
14728
  });
14970
14729
  } };
14971
14730
  }
@@ -15129,7 +14888,7 @@ const hooksNoNanInDeps = defineRule({
15129
14888
  });
15130
14889
  //#endregion
15131
14890
  //#region src/plugin/rules/a11y/html-has-lang.ts
15132
- const MESSAGE$52 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
14891
+ const MESSAGE$51 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
15133
14892
  const resolveSettings$39 = (settings) => {
15134
14893
  const reactDoctor = settings?.["react-doctor"];
15135
14894
  return { htmlTags: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.htmlHasLang ?? {} : {}).htmlTags ?? ["html"] };
@@ -15176,13 +14935,13 @@ const htmlHasLang = defineRule({
15176
14935
  if (!lang) {
15177
14936
  context.report({
15178
14937
  node: node.name,
15179
- message: MESSAGE$52
14938
+ message: MESSAGE$51
15180
14939
  });
15181
14940
  return;
15182
14941
  }
15183
14942
  if (evaluateLang(lang.value) === "empty") context.report({
15184
14943
  node: lang,
15185
- message: MESSAGE$52
14944
+ message: MESSAGE$51
15186
14945
  });
15187
14946
  } };
15188
14947
  }
@@ -15422,7 +15181,7 @@ const htmlNoNestedInteractive = defineRule({
15422
15181
  });
15423
15182
  //#endregion
15424
15183
  //#region src/plugin/rules/a11y/iframe-has-title.ts
15425
- const MESSAGE$51 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15184
+ const MESSAGE$50 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15426
15185
  const evaluateTitleValue = (value) => {
15427
15186
  if (!value) return "missing";
15428
15187
  if (isNodeOfType(value, "Literal")) {
@@ -15462,14 +15221,14 @@ const iframeHasTitle = defineRule({
15462
15221
  if (!titleAttr) {
15463
15222
  if (hasSpread || tag === "iframe") context.report({
15464
15223
  node: node.name,
15465
- message: MESSAGE$51
15224
+ message: MESSAGE$50
15466
15225
  });
15467
15226
  return;
15468
15227
  }
15469
15228
  const verdict = evaluateTitleValue(titleAttr.value);
15470
15229
  if (verdict === "missing" || verdict === "empty") context.report({
15471
15230
  node: titleAttr,
15472
- message: MESSAGE$51
15231
+ message: MESSAGE$50
15473
15232
  });
15474
15233
  } })
15475
15234
  });
@@ -15594,7 +15353,7 @@ const iframeMissingSandbox = defineRule({
15594
15353
  });
15595
15354
  //#endregion
15596
15355
  //#region src/plugin/rules/a11y/img-redundant-alt.ts
15597
- const MESSAGE$50 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
15356
+ const MESSAGE$49 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
15598
15357
  const DEFAULT_COMPONENTS = ["img"];
15599
15358
  const DEFAULT_REDUNDANT_WORDS = [
15600
15359
  "image",
@@ -15659,7 +15418,7 @@ const imgRedundantAlt = defineRule({
15659
15418
  if (!altAttribute) return;
15660
15419
  if (altValueRedundant(altAttribute, settings.words)) context.report({
15661
15420
  node: altAttribute,
15662
- message: MESSAGE$50
15421
+ message: MESSAGE$49
15663
15422
  });
15664
15423
  } };
15665
15424
  }
@@ -17442,7 +17201,7 @@ const GLOBAL_BUILTIN_NAMES = new Set([
17442
17201
  ]);
17443
17202
  const STRING_PROTOTYPE_PATH = "String.prototype";
17444
17203
  const REGEXP_PROTOTYPE_PATH = "RegExp.prototype";
17445
- const getStaticStringValue$1 = (argument) => {
17204
+ const getStaticStringValue = (argument) => {
17446
17205
  if (!argument) return null;
17447
17206
  const unwrappedArgument = stripParenExpression(argument);
17448
17207
  if (isNodeOfType(unwrappedArgument, "Literal") && typeof unwrappedArgument.value === "string") return unwrappedArgument.value;
@@ -17450,7 +17209,7 @@ const getStaticStringValue$1 = (argument) => {
17450
17209
  return null;
17451
17210
  };
17452
17211
  const getEffectiveRegExpFlags = (patternArgument, flagsArgument) => {
17453
- if (flagsArgument) return getStaticStringValue$1(flagsArgument);
17212
+ if (flagsArgument) return getStaticStringValue(flagsArgument);
17454
17213
  if (!patternArgument) return "";
17455
17214
  const unwrappedPattern = stripParenExpression(patternArgument);
17456
17215
  if (isNodeOfType(unwrappedPattern, "Literal") && unwrappedPattern.value instanceof RegExp) return unwrappedPattern.value.flags;
@@ -17600,7 +17359,7 @@ const getCallRegExpHazard = (node, context, symbolCache) => {
17600
17359
  const mutationHazard = targetPath === "global" ? "globalRegExpReplaced" : "replaceAllIntegrityLost";
17601
17360
  const guardedPropertyName = targetPath === "global" ? "RegExp" : "replaceAll";
17602
17361
  if (isSinglePropertyMutation) {
17603
- const propertyName = getStaticStringValue$1(node.arguments?.[1]);
17362
+ const propertyName = getStaticStringValue(node.arguments?.[1]);
17604
17363
  return propertyName === null || propertyName === guardedPropertyName ? mutationHazard : "none";
17605
17364
  }
17606
17365
  if (!isPropertyCollectionMutation) return "none";
@@ -17629,7 +17388,7 @@ const getHoistableRegExpConstructionKind = (node, context) => {
17629
17388
  if (!STATEFUL_REGEXP_FLAGS_PATTERN.test(effectiveFlags)) return "stateless";
17630
17389
  return isSafeStatefulReplaceAllSearch(node, effectiveFlags, context) ? "statefulReplaceAll" : null;
17631
17390
  };
17632
- const MESSAGE$49 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
17391
+ const MESSAGE$48 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
17633
17392
  const jsHoistRegexp = defineRule({
17634
17393
  id: "js-hoist-regexp",
17635
17394
  title: "RegExp built inside a loop",
@@ -17646,7 +17405,7 @@ const jsHoistRegexp = defineRule({
17646
17405
  if (constructionKind === "statefulReplaceAll" && cachedEnvironmentHazard === "replaceAllIntegrityLost") return;
17647
17406
  context.report({
17648
17407
  node,
17649
- message: MESSAGE$49
17408
+ message: MESSAGE$48
17650
17409
  });
17651
17410
  };
17652
17411
  return createLoopAwareVisitors({
@@ -20266,7 +20025,7 @@ const jsxMaxDepth = defineRule({
20266
20025
  });
20267
20026
  //#endregion
20268
20027
  //#region src/plugin/rules/react-builtins/jsx-no-comment-textnodes.ts
20269
- const MESSAGE$48 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20028
+ const MESSAGE$47 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20270
20029
  const LITERAL_TEXT_TAGS = new Set([
20271
20030
  "code",
20272
20031
  "pre",
@@ -20336,7 +20095,7 @@ const jsxNoCommentTextnodes = defineRule({
20336
20095
  if (isDeliberateStyledCommentToken(node)) return;
20337
20096
  context.report({
20338
20097
  node,
20339
- message: MESSAGE$48
20098
+ message: MESSAGE$47
20340
20099
  });
20341
20100
  } })
20342
20101
  });
@@ -20367,7 +20126,7 @@ const isInsideFunctionScope = (node) => {
20367
20126
  };
20368
20127
  //#endregion
20369
20128
  //#region src/plugin/rules/react-builtins/jsx-no-constructed-context-values.ts
20370
- const MESSAGE$47 = "Every reader of this context redraws on each render because you build its `value` inline.";
20129
+ const MESSAGE$46 = "Every reader of this context redraws on each render because you build its `value` inline.";
20371
20130
  const CONTEXT_MODULES$1 = [
20372
20131
  "react",
20373
20132
  "use-context-selector",
@@ -20465,7 +20224,7 @@ const jsxNoConstructedContextValues = defineRule({
20465
20224
  if (!isConstructedValue(innerExpression)) continue;
20466
20225
  context.report({
20467
20226
  node: attribute,
20468
- message: MESSAGE$47
20227
+ message: MESSAGE$46
20469
20228
  });
20470
20229
  }
20471
20230
  }
@@ -21320,7 +21079,7 @@ const DATA_ARRAY_PROP_SUFFIXES = [
21320
21079
  ];
21321
21080
  //#endregion
21322
21081
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop.ts
21323
- const MESSAGE$46 = "This child redraws every render because the prop gets a brand new array each time.";
21082
+ const MESSAGE$45 = "This child redraws every render because the prop gets a brand new array each time.";
21324
21083
  const isDataArrayPropName = (propName) => {
21325
21084
  if (DATA_ARRAY_PROP_NAMES.has(propName)) return true;
21326
21085
  for (const suffix of DATA_ARRAY_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -21407,7 +21166,7 @@ const jsxNoNewArrayAsProp = defineRule({
21407
21166
  if (!isArrayProducingExpression(expressionNode) && !followsRenderLocalArrayBinding(expressionNode, node)) return;
21408
21167
  context.report({
21409
21168
  node,
21410
- message: MESSAGE$46
21169
+ message: MESSAGE$45
21411
21170
  });
21412
21171
  }
21413
21172
  };
@@ -21665,7 +21424,7 @@ const SAFE_RECEIVER_NAMES = new Set([
21665
21424
  ]);
21666
21425
  //#endregion
21667
21426
  //#region src/plugin/rules/react-builtins/jsx-no-new-function-as-prop.ts
21668
- const MESSAGE$45 = "This child redraws every render because the prop gets a brand new function each time.";
21427
+ const MESSAGE$44 = "This child redraws every render because the prop gets a brand new function each time.";
21669
21428
  const isAccessorPredicateName = (propName) => {
21670
21429
  for (const prefix of ACCESSOR_PREDICATE_PREFIXES) {
21671
21430
  if (propName.length <= prefix.length) continue;
@@ -21871,7 +21630,7 @@ const jsxNoNewFunctionAsProp = defineRule({
21871
21630
  if (!isFunctionProducingExpression(expressionNode) && !followsRenderLocalFunctionBinding(expressionNode, node)) return;
21872
21631
  context.report({
21873
21632
  node,
21874
- message: MESSAGE$45
21633
+ message: MESSAGE$44
21875
21634
  });
21876
21635
  }
21877
21636
  };
@@ -22091,7 +21850,7 @@ const CONFIG_OBJECT_PROP_SUFFIXES = [
22091
21850
  ];
22092
21851
  //#endregion
22093
21852
  //#region src/plugin/rules/react-builtins/jsx-no-new-object-as-prop.ts
22094
- const MESSAGE$44 = "This child redraws every render because the prop gets a brand new object each time.";
21853
+ const MESSAGE$43 = "This child redraws every render because the prop gets a brand new object each time.";
22095
21854
  const isConfigObjectPropName = (propName) => {
22096
21855
  if (CONFIG_OBJECT_PROP_NAMES.has(propName)) return true;
22097
21856
  for (const suffix of CONFIG_OBJECT_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -22180,7 +21939,7 @@ const jsxNoNewObjectAsProp = defineRule({
22180
21939
  if (!isObjectProducingExpression(expressionNode) && !followsRenderLocalObjectBinding(expressionNode, node)) return;
22181
21940
  context.report({
22182
21941
  node,
22183
- message: MESSAGE$44
21942
+ message: MESSAGE$43
22184
21943
  });
22185
21944
  }
22186
21945
  };
@@ -22188,7 +21947,7 @@ const jsxNoNewObjectAsProp = defineRule({
22188
21947
  });
22189
21948
  //#endregion
22190
21949
  //#region src/plugin/rules/react-builtins/jsx-no-script-url.ts
22191
- const MESSAGE$43 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
21950
+ const MESSAGE$42 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
22192
21951
  const JAVASCRIPT_URL_PATTERN = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
22193
21952
  const resolveSettings$29 = (settings) => {
22194
21953
  const reactDoctor = settings?.["react-doctor"];
@@ -22226,7 +21985,7 @@ const jsxNoScriptUrl = defineRule({
22226
21985
  if (!value || !isNodeOfType(value, "Literal") || typeof value.value !== "string") continue;
22227
21986
  if (JAVASCRIPT_URL_PATTERN.test(value.value)) context.report({
22228
21987
  node: attribute,
22229
- message: MESSAGE$43
21988
+ message: MESSAGE$42
22230
21989
  });
22231
21990
  }
22232
21991
  } };
@@ -22846,7 +22605,7 @@ const jsxPropsNoSpreadMulti = defineRule({
22846
22605
  });
22847
22606
  //#endregion
22848
22607
  //#region src/plugin/rules/react-builtins/jsx-props-no-spreading.ts
22849
- const MESSAGE$42 = "You can't tell what props reach this element when you spread them.";
22608
+ const MESSAGE$41 = "You can't tell what props reach this element when you spread them.";
22850
22609
  const resolveSettings$25 = (settings) => {
22851
22610
  const reactDoctor = settings?.["react-doctor"];
22852
22611
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxPropsNoSpreading ?? {} : {};
@@ -22889,7 +22648,7 @@ const jsxPropsNoSpreading = defineRule({
22889
22648
  }
22890
22649
  context.report({
22891
22650
  node: attribute,
22892
- message: MESSAGE$42
22651
+ message: MESSAGE$41
22893
22652
  });
22894
22653
  didReportInFile = true;
22895
22654
  return;
@@ -23165,7 +22924,7 @@ const labelHasAssociatedControl = defineRule({
23165
22924
  });
23166
22925
  //#endregion
23167
22926
  //#region src/plugin/rules/a11y/lang.ts
23168
- const MESSAGE$41 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
22927
+ const MESSAGE$40 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
23169
22928
  const COMMON_LANGUAGE_PRIMARY_TAGS = new Set([
23170
22929
  "aa",
23171
22930
  "ab",
@@ -23645,7 +23404,7 @@ const lang = defineRule({
23645
23404
  if (expression.type === "Identifier" && expression.name === "undefined" || expression.type === "Literal" && expression.value === null) {
23646
23405
  context.report({
23647
23406
  node: langAttr,
23648
- message: MESSAGE$41
23407
+ message: MESSAGE$40
23649
23408
  });
23650
23409
  return;
23651
23410
  }
@@ -23654,7 +23413,7 @@ const lang = defineRule({
23654
23413
  if (value === null) return;
23655
23414
  if (!isValidLangTag(value)) context.report({
23656
23415
  node: langAttr,
23657
- message: MESSAGE$41
23416
+ message: MESSAGE$40
23658
23417
  });
23659
23418
  } })
23660
23419
  });
@@ -23705,7 +23464,7 @@ const mdxSsrExecutionRisk = defineRule({
23705
23464
  });
23706
23465
  //#endregion
23707
23466
  //#region src/plugin/rules/a11y/media-has-caption.ts
23708
- const MESSAGE$40 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
23467
+ const MESSAGE$39 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
23709
23468
  const DEFAULT_AUDIO = ["audio"];
23710
23469
  const DEFAULT_VIDEO = ["video"];
23711
23470
  const DEFAULT_TRACK = ["track"];
@@ -23794,7 +23553,7 @@ const mediaHasCaption = defineRule({
23794
23553
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
23795
23554
  context.report({
23796
23555
  node: node.name,
23797
- message: MESSAGE$40
23556
+ message: MESSAGE$39
23798
23557
  });
23799
23558
  return;
23800
23559
  }
@@ -23813,7 +23572,7 @@ const mediaHasCaption = defineRule({
23813
23572
  return kindValue.value.toLowerCase() === "captions";
23814
23573
  })) context.report({
23815
23574
  node: node.name,
23816
- message: MESSAGE$40
23575
+ message: MESSAGE$39
23817
23576
  });
23818
23577
  } };
23819
23578
  }
@@ -25356,7 +25115,7 @@ const nextjsNoVercelOgImport = defineRule({
25356
25115
  });
25357
25116
  //#endregion
25358
25117
  //#region src/plugin/rules/a11y/no-access-key.ts
25359
- const MESSAGE$39 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25118
+ const MESSAGE$38 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25360
25119
  const isUndefinedIdentifier = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
25361
25120
  const noAccessKey = defineRule({
25362
25121
  id: "no-access-key",
@@ -25375,7 +25134,7 @@ const noAccessKey = defineRule({
25375
25134
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
25376
25135
  context.report({
25377
25136
  node: accessKey,
25378
- message: MESSAGE$39
25137
+ message: MESSAGE$38
25379
25138
  });
25380
25139
  return;
25381
25140
  }
@@ -25385,7 +25144,7 @@ const noAccessKey = defineRule({
25385
25144
  if (isUndefinedIdentifier(expression)) return;
25386
25145
  context.report({
25387
25146
  node: accessKey,
25388
- message: MESSAGE$39
25147
+ message: MESSAGE$38
25389
25148
  });
25390
25149
  }
25391
25150
  } };
@@ -27240,7 +26999,7 @@ const noAdjustStateOnPropChange = defineRule({
27240
26999
  });
27241
27000
  //#endregion
27242
27001
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
27243
- const MESSAGE$38 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
27002
+ const MESSAGE$37 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
27244
27003
  const ALWAYS_FOCUSABLE_TAGS = new Set([
27245
27004
  "button",
27246
27005
  "embed",
@@ -27352,7 +27111,7 @@ const noAriaHiddenOnFocusable = defineRule({
27352
27111
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
27353
27112
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
27354
27113
  node: ariaHidden,
27355
- message: MESSAGE$38
27114
+ message: MESSAGE$37
27356
27115
  });
27357
27116
  } })
27358
27117
  });
@@ -28274,7 +28033,7 @@ const isAllLiteralArrayExpression = (node) => {
28274
28033
  };
28275
28034
  //#endregion
28276
28035
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
28277
- const MESSAGE$37 = "Your users can see & submit the wrong data when this list reorders.";
28036
+ const MESSAGE$36 = "Your users can see & submit the wrong data when this list reorders.";
28278
28037
  const SECOND_INDEX_METHODS = new Set([
28279
28038
  "every",
28280
28039
  "filter",
@@ -28393,14 +28152,14 @@ const noArrayIndexKey = defineRule({
28393
28152
  if (propName !== "key") continue;
28394
28153
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
28395
28154
  node: property,
28396
- message: MESSAGE$37
28155
+ message: MESSAGE$36
28397
28156
  });
28398
28157
  }
28399
28158
  } })
28400
28159
  });
28401
28160
  //#endregion
28402
28161
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
28403
- const MESSAGE$36 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
28162
+ const MESSAGE$35 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
28404
28163
  const noAsyncEffectCallback = defineRule({
28405
28164
  id: "no-async-effect-callback",
28406
28165
  title: "Async effect callback",
@@ -28418,13 +28177,13 @@ const noAsyncEffectCallback = defineRule({
28418
28177
  if (!callback.async) return;
28419
28178
  context.report({
28420
28179
  node: callback,
28421
- message: MESSAGE$36
28180
+ message: MESSAGE$35
28422
28181
  });
28423
28182
  } })
28424
28183
  });
28425
28184
  //#endregion
28426
28185
  //#region src/plugin/rules/a11y/no-autofocus.ts
28427
- const MESSAGE$35 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
28186
+ const MESSAGE$34 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
28428
28187
  const resolveSettings$21 = (settings) => {
28429
28188
  const reactDoctor = settings?.["react-doctor"];
28430
28189
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -28521,7 +28280,7 @@ const noAutofocus = defineRule({
28521
28280
  if (isConditionallyRendered(node)) return;
28522
28281
  context.report({
28523
28282
  node: autoFocusAttribute,
28524
- message: MESSAGE$35
28283
+ message: MESSAGE$34
28525
28284
  });
28526
28285
  } };
28527
28286
  }
@@ -29494,7 +29253,7 @@ const noChainStateUpdates = defineRule({
29494
29253
  });
29495
29254
  //#endregion
29496
29255
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
29497
- const MESSAGE$34 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29256
+ const MESSAGE$33 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29498
29257
  const noChildrenProp = defineRule({
29499
29258
  id: "no-children-prop",
29500
29259
  title: "Children passed as a prop",
@@ -29506,7 +29265,7 @@ const noChildrenProp = defineRule({
29506
29265
  if (node.name.name !== "children") return;
29507
29266
  context.report({
29508
29267
  node: node.name,
29509
- message: MESSAGE$34
29268
+ message: MESSAGE$33
29510
29269
  });
29511
29270
  },
29512
29271
  CallExpression(node) {
@@ -29519,7 +29278,7 @@ const noChildrenProp = defineRule({
29519
29278
  const propertyKey = property.key;
29520
29279
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
29521
29280
  node: propertyKey,
29522
- message: MESSAGE$34
29281
+ message: MESSAGE$33
29523
29282
  });
29524
29283
  }
29525
29284
  }
@@ -29527,7 +29286,7 @@ const noChildrenProp = defineRule({
29527
29286
  });
29528
29287
  //#endregion
29529
29288
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
29530
- const MESSAGE$33 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29289
+ const MESSAGE$32 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29531
29290
  const noCloneElement = defineRule({
29532
29291
  id: "no-clone-element",
29533
29292
  title: "cloneElement makes child props fragile",
@@ -29540,7 +29299,7 @@ const noCloneElement = defineRule({
29540
29299
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
29541
29300
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
29542
29301
  node: callee,
29543
- message: MESSAGE$33
29302
+ message: MESSAGE$32
29544
29303
  });
29545
29304
  return;
29546
29305
  }
@@ -29553,7 +29312,7 @@ const noCloneElement = defineRule({
29553
29312
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
29554
29313
  context.report({
29555
29314
  node: callee,
29556
- message: MESSAGE$33
29315
+ message: MESSAGE$32
29557
29316
  });
29558
29317
  }
29559
29318
  } })
@@ -29573,7 +29332,7 @@ const getCallMethodName = (callee) => {
29573
29332
  };
29574
29333
  //#endregion
29575
29334
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
29576
- const MESSAGE$32 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29335
+ const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29577
29336
  const CONTEXT_MODULES = [
29578
29337
  "react",
29579
29338
  "use-context-selector",
@@ -29621,13 +29380,13 @@ const noCreateContextInRender = defineRule({
29621
29380
  if (!componentOrHookName) return;
29622
29381
  context.report({
29623
29382
  node,
29624
- message: `${MESSAGE$32} (called inside "${componentOrHookName}")`
29383
+ message: `${MESSAGE$31} (called inside "${componentOrHookName}")`
29625
29384
  });
29626
29385
  } })
29627
29386
  });
29628
29387
  //#endregion
29629
29388
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
29630
- const MESSAGE$31 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
29389
+ const MESSAGE$30 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
29631
29390
  const isUseMemoCallbackArgument = (functionNode) => {
29632
29391
  const parent = functionNode.parent;
29633
29392
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -29657,7 +29416,7 @@ const noCreateRefInFunctionComponent = defineRule({
29657
29416
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes, context.cfg))) return;
29658
29417
  context.report({
29659
29418
  node,
29660
- message: MESSAGE$31
29419
+ message: MESSAGE$30
29661
29420
  });
29662
29421
  } })
29663
29422
  });
@@ -29797,7 +29556,7 @@ const noCreateStoreInRender = defineRule({
29797
29556
  });
29798
29557
  //#endregion
29799
29558
  //#region src/plugin/rules/react-builtins/no-danger.ts
29800
- const MESSAGE$30 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
29559
+ const MESSAGE$29 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
29801
29560
  const noDanger = defineRule({
29802
29561
  id: "no-danger",
29803
29562
  title: "Raw HTML injection can run unsafe markup",
@@ -29811,7 +29570,7 @@ const noDanger = defineRule({
29811
29570
  if (!propAttribute) return;
29812
29571
  context.report({
29813
29572
  node: propAttribute.name,
29814
- message: MESSAGE$30
29573
+ message: MESSAGE$29
29815
29574
  });
29816
29575
  },
29817
29576
  CallExpression(node) {
@@ -29823,7 +29582,7 @@ const noDanger = defineRule({
29823
29582
  const propertyKey = property.key;
29824
29583
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
29825
29584
  node: propertyKey,
29826
- message: MESSAGE$30
29585
+ message: MESSAGE$29
29827
29586
  });
29828
29587
  }
29829
29588
  }
@@ -29846,7 +29605,7 @@ const isMeaningfulJsxChild = (child) => {
29846
29605
  };
29847
29606
  //#endregion
29848
29607
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
29849
- const MESSAGE$29 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
29608
+ const MESSAGE$28 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
29850
29609
  const mergePropsShape = (target, source) => {
29851
29610
  target.hasDangerously ||= source.hasDangerously;
29852
29611
  target.hasChildren ||= source.hasChildren;
@@ -29921,7 +29680,7 @@ const noDangerWithChildren = defineRule({
29921
29680
  if (!hasChildrenProp && !hasNestedChildren) return;
29922
29681
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
29923
29682
  node: opening,
29924
- message: MESSAGE$29
29683
+ message: MESSAGE$28
29925
29684
  });
29926
29685
  },
29927
29686
  CallExpression(node) {
@@ -29934,7 +29693,7 @@ const noDangerWithChildren = defineRule({
29934
29693
  const positionalChildren = node.arguments.slice(2);
29935
29694
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
29936
29695
  node,
29937
- message: MESSAGE$29
29696
+ message: MESSAGE$28
29938
29697
  });
29939
29698
  }
29940
29699
  })
@@ -30899,7 +30658,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
30899
30658
  //#endregion
30900
30659
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
30901
30660
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
30902
- const MESSAGE$28 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
30661
+ const MESSAGE$27 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
30903
30662
  const getNodeStart = (node) => "start" in node && typeof node.start === "number" ? node.start : -1;
30904
30663
  const getEnclosingLifecycleFunction = (setStateCall) => {
30905
30664
  let ancestor = setStateCall.parent;
@@ -31027,7 +30786,7 @@ const noDidMountSetState = defineRule({
31027
30786
  }
31028
30787
  context.report({
31029
30788
  node: node.callee,
31030
- message: MESSAGE$28
30789
+ message: MESSAGE$27
31031
30790
  });
31032
30791
  } };
31033
30792
  }
@@ -31035,7 +30794,7 @@ const noDidMountSetState = defineRule({
31035
30794
  //#endregion
31036
30795
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
31037
30796
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
31038
- const MESSAGE$27 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
30797
+ const MESSAGE$26 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
31039
30798
  const EQUALITY_OPERATORS = new Set([
31040
30799
  "==",
31041
30800
  "===",
@@ -31209,7 +30968,7 @@ const noDidUpdateSetState = defineRule({
31209
30968
  if (isInsideDiffGuard(node)) return;
31210
30969
  context.report({
31211
30970
  node: node.callee,
31212
- message: MESSAGE$27
30971
+ message: MESSAGE$26
31213
30972
  });
31214
30973
  } };
31215
30974
  }
@@ -31232,7 +30991,7 @@ const isStateMemberExpression = (node) => {
31232
30991
  };
31233
30992
  //#endregion
31234
30993
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
31235
- const MESSAGE$26 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
30994
+ const MESSAGE$25 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
31236
30995
  const shouldIgnoreMutation = (node) => {
31237
30996
  let isConstructor = false;
31238
30997
  let isInsideCallExpression = false;
@@ -31254,7 +31013,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
31254
31013
  if (shouldIgnoreMutation(reportNode)) return;
31255
31014
  context.report({
31256
31015
  node: reportNode,
31257
- message: MESSAGE$26
31016
+ message: MESSAGE$25
31258
31017
  });
31259
31018
  };
31260
31019
  const noDirectMutationState = defineRule({
@@ -31605,7 +31364,7 @@ const noDocumentStartViewTransition = defineRule({
31605
31364
  });
31606
31365
  //#endregion
31607
31366
  //#region src/plugin/rules/js-performance/no-document-write.ts
31608
- const MESSAGE$25 = "`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.";
31367
+ 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.";
31609
31368
  const WRITE_METHODS = new Set(["write", "writeln"]);
31610
31369
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
31611
31370
  const noDocumentWrite = defineRule({
@@ -31622,7 +31381,7 @@ const noDocumentWrite = defineRule({
31622
31381
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
31623
31382
  context.report({
31624
31383
  node,
31625
- message: MESSAGE$25
31384
+ message: MESSAGE$24
31626
31385
  });
31627
31386
  } })
31628
31387
  });
@@ -33840,7 +33599,7 @@ const ALLOWED_NAMESPACES = new Set([
33840
33599
  "ReactDOM",
33841
33600
  "ReactDom"
33842
33601
  ]);
33843
- const MESSAGE$24 = "`findDOMNode` crashes your app in React 19 because it was removed.";
33602
+ const MESSAGE$23 = "`findDOMNode` crashes your app in React 19 because it was removed.";
33844
33603
  const noFindDomNode = defineRule({
33845
33604
  id: "no-find-dom-node",
33846
33605
  title: "findDOMNode breaks component encapsulation",
@@ -33851,7 +33610,7 @@ const noFindDomNode = defineRule({
33851
33610
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
33852
33611
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
33853
33612
  node: callee,
33854
- message: MESSAGE$24
33613
+ message: MESSAGE$23
33855
33614
  });
33856
33615
  return;
33857
33616
  }
@@ -33862,7 +33621,7 @@ const noFindDomNode = defineRule({
33862
33621
  if (callee.property.name !== "findDOMNode") return;
33863
33622
  context.report({
33864
33623
  node: callee.property,
33865
- message: MESSAGE$24
33624
+ message: MESSAGE$23
33866
33625
  });
33867
33626
  }
33868
33627
  } })
@@ -34071,6 +33830,13 @@ const isPublishedLibraryPackage = (filename) => {
34071
33830
  return manifest.private !== true && typeof manifest.peerDependencies === "object" && manifest.peerDependencies !== null && "react" in manifest.peerDependencies;
34072
33831
  };
34073
33832
  //#endregion
33833
+ //#region src/plugin/utils/is-type-only-import.ts
33834
+ const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
33835
+ if (!specifiers || specifiers.length === 0) return false;
33836
+ return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
33837
+ };
33838
+ const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
33839
+ //#endregion
34074
33840
  //#region src/plugin/rules/bundle-size/no-full-lodash-import.ts
34075
33841
  const isLibraryDevPage = (filename) => {
34076
33842
  if (!filename) return false;
@@ -34769,7 +34535,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
34769
34535
  });
34770
34536
  //#endregion
34771
34537
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
34772
- const MESSAGE$23 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
34538
+ const MESSAGE$22 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
34773
34539
  const noImgLazyWithHighFetchpriority = defineRule({
34774
34540
  id: "no-img-lazy-with-high-fetchpriority",
34775
34541
  title: "Lazy image with high fetchPriority",
@@ -34783,7 +34549,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
34783
34549
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
34784
34550
  context.report({
34785
34551
  node: node.name,
34786
- message: MESSAGE$23
34552
+ message: MESSAGE$22
34787
34553
  });
34788
34554
  } })
34789
34555
  });
@@ -34972,7 +34738,7 @@ const noImpureStateUpdater = defineRule({
34972
34738
  });
34973
34739
  //#endregion
34974
34740
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
34975
- const MESSAGE$22 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
34741
+ const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
34976
34742
  const REACT_USE_REF_OPTIONS = {
34977
34743
  allowGlobalReactNamespace: false,
34978
34744
  allowUnboundBareCalls: false
@@ -35073,7 +34839,7 @@ const noIndeterminateAttribute = defineRule({
35073
34839
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
35074
34840
  context.report({
35075
34841
  node: indeterminateAttribute,
35076
- message: MESSAGE$22
34842
+ message: MESSAGE$21
35077
34843
  });
35078
34844
  },
35079
34845
  CallExpression(node) {
@@ -35082,7 +34848,7 @@ const noIndeterminateAttribute = defineRule({
35082
34848
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
35083
34849
  context.report({
35084
34850
  node,
35085
- message: MESSAGE$22
34851
+ message: MESSAGE$21
35086
34852
  });
35087
34853
  }
35088
34854
  };
@@ -35370,7 +35136,7 @@ const noIsMounted = defineRule({
35370
35136
  });
35371
35137
  //#endregion
35372
35138
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
35373
- const MESSAGE$21 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
35139
+ const MESSAGE$20 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
35374
35140
  const isJsonMethodCall = (node, method) => {
35375
35141
  if (!isNodeOfType(node, "CallExpression")) return false;
35376
35142
  const callee = node.callee;
@@ -35434,13 +35200,13 @@ const noJsonParseStringifyClone = defineRule({
35434
35200
  if (isCatchParameterRoundTrip(firstArgument)) return;
35435
35201
  context.report({
35436
35202
  node,
35437
- message: MESSAGE$21
35203
+ message: MESSAGE$20
35438
35204
  });
35439
35205
  } })
35440
35206
  });
35441
35207
  //#endregion
35442
35208
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
35443
- const MESSAGE$20 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35209
+ const MESSAGE$19 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35444
35210
  const isJsxElementTypeReference = (node) => {
35445
35211
  if (!isNodeOfType(node, "TSTypeReference")) return false;
35446
35212
  const typeName = node.typeName;
@@ -35494,7 +35260,7 @@ const noJsxElementType = defineRule({
35494
35260
  if (isJsxImported) return;
35495
35261
  for (const typeAnnotation of flaggedAnnotations) context.report({
35496
35262
  node: typeAnnotation,
35497
- message: MESSAGE$20
35263
+ message: MESSAGE$19
35498
35264
  });
35499
35265
  }
35500
35266
  };
@@ -35726,221 +35492,6 @@ const noLegacyClassLifecycles = defineRule({
35726
35492
  }
35727
35493
  });
35728
35494
  //#endregion
35729
- //#region src/plugin/utils/is-proven-react-class-component.ts
35730
- const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
35731
- const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
35732
- const expression = stripParenExpression(node);
35733
- if (isNodeOfType(expression, "MemberExpression")) {
35734
- const propertyName = getStaticPropertyName(expression);
35735
- const receiver = stripParenExpression(expression.object);
35736
- return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
35737
- }
35738
- if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
35739
- if (!isNodeOfType(expression, "Identifier")) return false;
35740
- const symbol = scopes.symbolFor(expression);
35741
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
35742
- visitedSymbolIds.add(symbol.id);
35743
- if (isImportedFromReact(symbol)) {
35744
- const importedName = getImportedName(symbol.declarationNode);
35745
- return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
35746
- }
35747
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
35748
- return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
35749
- };
35750
- const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
35751
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
35752
- visitedClassNodes.add(classNode);
35753
- return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
35754
- };
35755
- //#endregion
35756
- //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
35757
- const functionContainsProvenReactHookCall = (functionNode, scopes) => {
35758
- if (!isFunctionLike$2(functionNode)) return false;
35759
- let containsReactHookCall = false;
35760
- walkAst(functionNode.body, (node) => {
35761
- if (containsReactHookCall) return false;
35762
- if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
35763
- if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
35764
- containsReactHookCall = true;
35765
- return false;
35766
- }
35767
- });
35768
- return containsReactHookCall;
35769
- };
35770
- //#endregion
35771
- //#region src/plugin/utils/function-returns-props-children.ts
35772
- const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
35773
- if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
35774
- const firstParameter = stripParenExpression(functionNode.params[0]);
35775
- const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
35776
- const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
35777
- const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
35778
- if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
35779
- for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
35780
- const propertyValue = stripParenExpression(property.value);
35781
- const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
35782
- if (!isNodeOfType(childrenBinding, "Identifier")) continue;
35783
- const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
35784
- if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
35785
- }
35786
- }
35787
- return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
35788
- const candidate = stripParenExpression(expression);
35789
- if (isNodeOfType(candidate, "Identifier")) {
35790
- const symbol = scopes.symbolFor(candidate);
35791
- return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
35792
- }
35793
- if (!isNodeOfType(candidate, "MemberExpression")) return false;
35794
- if (getStaticPropertyName(candidate) !== "children") return false;
35795
- const receiver = stripParenExpression(candidate.object);
35796
- if (!isNodeOfType(receiver, "Identifier")) return false;
35797
- const receiverSymbol = scopes.symbolFor(receiver);
35798
- if (!receiverSymbol || !propsParameterSymbol) return false;
35799
- return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
35800
- }, controlFlow);
35801
- };
35802
- //#endregion
35803
- //#region src/plugin/utils/function-returns-only-null.ts
35804
- const isNullExpression = (expression) => {
35805
- const candidate = stripParenExpression(expression);
35806
- return isNodeOfType(candidate, "Literal") && candidate.value === null;
35807
- };
35808
- const functionReturnsOnlyNull = (functionNode) => {
35809
- if (!isFunctionLike$2(functionNode)) return false;
35810
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
35811
- const returnStatements = collectFunctionReturnStatements(functionNode);
35812
- return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
35813
- };
35814
- //#endregion
35815
- //#region src/plugin/utils/is-proven-styled-component-expression.ts
35816
- const findFactoryRoot = (node) => {
35817
- const candidate = stripParenExpression(node);
35818
- if (isNodeOfType(candidate, "Identifier")) return candidate;
35819
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
35820
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
35821
- return null;
35822
- };
35823
- const findFactoryPropertyName = (node) => {
35824
- const candidate = stripParenExpression(node);
35825
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
35826
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
35827
- return null;
35828
- };
35829
- const isProvenStyledComponentExpression = (expression, scopes) => {
35830
- const candidate = stripParenExpression(expression);
35831
- if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
35832
- const factoryRoot = findFactoryRoot(candidate.tag);
35833
- if (!factoryRoot) return false;
35834
- const factoryPropertyName = findFactoryPropertyName(candidate.tag);
35835
- if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
35836
- const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
35837
- if (!symbol || symbol.kind !== "import") return false;
35838
- if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
35839
- const importDeclaration = symbol.declarationNode.parent;
35840
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
35841
- };
35842
- //#endregion
35843
- //#region src/plugin/utils/is-proven-react-component-symbol.ts
35844
- const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
35845
- const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
35846
- const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
35847
- const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
35848
- const candidate = stripParenExpression(expression);
35849
- if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
35850
- if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
35851
- if (isProvenStyledComponentExpression(candidate, scopes)) return true;
35852
- if (isNodeOfType(candidate, "Identifier")) {
35853
- const symbol = scopes.symbolFor(candidate);
35854
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
35855
- visitedSymbolIds.add(symbol.id);
35856
- if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
35857
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
35858
- return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
35859
- }
35860
- if (!isNodeOfType(candidate, "CallExpression")) return false;
35861
- if (!hasStableCallTarget(candidate, scopes)) return false;
35862
- const factoryCallee = stripParenExpression(candidate.callee);
35863
- if (isNodeOfType(factoryCallee, "Identifier") && isDefaultImportFromModule(factoryCallee, factoryCallee.name, "create-react-class") || isReactApiCall(candidate, LEGACY_REACT_COMPONENT_FACTORY_NAMES, scopes)) return true;
35864
- if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
35865
- const wrappedComponent = candidate.arguments[0];
35866
- return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
35867
- }
35868
- if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
35869
- const factory = candidate.arguments[0];
35870
- if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
35871
- const unwrappedFactory = stripParenExpression(factory);
35872
- if (!isInlineFunctionExpression(unwrappedFactory)) return false;
35873
- if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
35874
- const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
35875
- const returnedExpression = returnStatements[0]?.argument;
35876
- return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
35877
- };
35878
- const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
35879
- const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
35880
- for (const candidateSymbol of candidateSymbols) {
35881
- if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
35882
- if (isComponentDeclaration(candidateSymbol.declarationNode)) {
35883
- if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
35884
- continue;
35885
- }
35886
- const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
35887
- if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
35888
- if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
35889
- continue;
35890
- }
35891
- if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
35892
- if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
35893
- continue;
35894
- }
35895
- if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
35896
- }
35897
- return false;
35898
- };
35899
- //#endregion
35900
- //#region src/plugin/utils/symbol-has-react-component-type-annotation.ts
35901
- const REACT_COMPONENT_TYPE_NAMES = new Set([
35902
- "ComponentClass",
35903
- "ComponentType",
35904
- "FC",
35905
- "FunctionComponent"
35906
- ]);
35907
- const findVisibleSymbol = (identifier, scopes) => {
35908
- if (!isNodeOfType(identifier, "Identifier")) return null;
35909
- let scope = scopes.scopeFor(identifier);
35910
- while (true) {
35911
- const symbol = scope.symbolsByName.get(identifier.name);
35912
- if (symbol) return symbol;
35913
- if (!scope.parent) return null;
35914
- scope = scope.parent;
35915
- }
35916
- };
35917
- const isReactNamespaceType = (identifier, scopes) => {
35918
- const symbol = findVisibleSymbol(identifier, scopes);
35919
- return Boolean(symbol && isImportedFromReact(symbol) && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
35920
- };
35921
- const isReactComponentType = (typeNode, scopes, visitedSymbolIds) => {
35922
- if (!typeNode) return false;
35923
- if (isNodeOfType(typeNode, "TSTypeAnnotation")) return isReactComponentType(typeNode.typeAnnotation, scopes, visitedSymbolIds);
35924
- if (isNodeOfType(typeNode, "TSIntersectionType")) return (typeNode.types ?? []).some((member) => isReactComponentType(member, scopes, visitedSymbolIds));
35925
- if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
35926
- const typeName = typeNode.typeName;
35927
- if (isNodeOfType(typeName, "TSQualifiedName")) return isNodeOfType(typeName.left, "Identifier") && isNodeOfType(typeName.right, "Identifier") && REACT_COMPONENT_TYPE_NAMES.has(typeName.right.name) && isReactNamespaceType(typeName.left, scopes);
35928
- if (!isNodeOfType(typeName, "Identifier")) return false;
35929
- const typeSymbol = findVisibleSymbol(typeName, scopes);
35930
- if (!typeSymbol || visitedSymbolIds.has(typeSymbol.id)) return false;
35931
- if (isImportedFromReact(typeSymbol)) {
35932
- const importedName = getImportedName(typeSymbol.declarationNode);
35933
- return Boolean(importedName && REACT_COMPONENT_TYPE_NAMES.has(importedName));
35934
- }
35935
- if (!isNodeOfType(typeSymbol.declarationNode, "TSTypeAliasDeclaration")) return false;
35936
- visitedSymbolIds.add(typeSymbol.id);
35937
- return isReactComponentType(typeSymbol.declarationNode.typeAnnotation, scopes, visitedSymbolIds);
35938
- };
35939
- const symbolHasReactComponentTypeAnnotation = (symbol, scopes) => {
35940
- const bindingIdentifier = symbol.bindingIdentifier;
35941
- return isNodeOfType(bindingIdentifier, "Identifier") && isReactComponentType(bindingIdentifier.typeAnnotation, scopes, /* @__PURE__ */ new Set());
35942
- };
35943
- //#endregion
35944
35495
  //#region src/plugin/rules/architecture/no-legacy-context-api.ts
35945
35496
  const LEGACY_CONTEXT_NAMES = new Set([
35946
35497
  "childContextTypes",
@@ -35951,6 +35502,15 @@ const buildLegacyContextMessage = (memberName) => {
35951
35502
  if (memberName === "childContextTypes" || memberName === "getChildContext") return `${memberName} uses the old context API that React 19 removes, so your provider stops passing data. Switch to \`createContext\` with \`<MyContext.Provider value={...}>\` & read it with \`useContext()\`, moving every consumer together.`;
35952
35503
  return "contextTypes uses the old context API that React 19 removes, so your component stops receiving context. Use `static contextType = MyContext` or `useContext()` in a function component, & update the provider too.";
35953
35504
  };
35505
+ const isInsideClassBody = (node) => {
35506
+ let current = node.parent;
35507
+ while (current) {
35508
+ if (isNodeOfType(current, "ClassBody")) return true;
35509
+ if (isFunctionLike$2(current)) return false;
35510
+ current = current.parent;
35511
+ }
35512
+ return false;
35513
+ };
35954
35514
  const noLegacyContextApi = defineRule({
35955
35515
  id: "no-legacy-context-api",
35956
35516
  title: "Legacy context API",
@@ -35965,7 +35525,6 @@ const noLegacyContextApi = defineRule({
35965
35525
  if (!isNodeOfType(memberNode, "MethodDefinition") && !isNodeOfType(memberNode, "PropertyDefinition")) return;
35966
35526
  if (!isNodeOfType(memberNode.key, "Identifier")) return;
35967
35527
  if (!LEGACY_CONTEXT_NAMES.has(memberNode.key.name)) return;
35968
- if (memberNode.key.name === "getChildContext" ? memberNode.static : !memberNode.static) return;
35969
35528
  context.report({
35970
35529
  node: memberNode.key,
35971
35530
  message: buildLegacyContextMessage(memberNode.key.name)
@@ -35973,8 +35532,6 @@ const noLegacyContextApi = defineRule({
35973
35532
  };
35974
35533
  return {
35975
35534
  ClassBody(node) {
35976
- const classNode = node.parent;
35977
- if (!classNode || !isProvenReactClassComponent(classNode, context.scopes)) return;
35978
35535
  for (const member of node.body ?? []) checkMember(member);
35979
35536
  },
35980
35537
  AssignmentExpression(node) {
@@ -35984,11 +35541,9 @@ const noLegacyContextApi = defineRule({
35984
35541
  if (left.computed) return;
35985
35542
  if (!isNodeOfType(left.property, "Identifier")) return;
35986
35543
  if (!LEGACY_CONTEXT_NAMES.has(left.property.name)) return;
35987
- if (left.property.name === "getChildContext") return;
35988
- const component = stripParenExpression(left.object);
35989
- if (!isNodeOfType(component, "Identifier")) return;
35990
- const symbol = context.scopes.symbolFor(component);
35991
- if (!symbol || !isProvenReactComponentSymbol(symbol, context.scopes, context.cfg, component) && (hasSymbolWriteBefore(symbol, component, context.scopes) || !symbolHasReactComponentTypeAnnotation(symbol, context.scopes))) return;
35544
+ if (!isNodeOfType(left.object, "Identifier")) return;
35545
+ if (!isUppercaseName(left.object.name)) return;
35546
+ if (isInsideClassBody(node)) return;
35992
35547
  context.report({
35993
35548
  node: left,
35994
35549
  message: buildLegacyContextMessage(left.property.name)
@@ -36810,7 +36365,7 @@ const noMoment = defineRule({
36810
36365
  });
36811
36366
  //#endregion
36812
36367
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
36813
- const MESSAGE$19 = "This file declares several components, so each component is harder to find, test, and change.";
36368
+ const MESSAGE$18 = "This file declares several components, so each component is harder to find, test, and change.";
36814
36369
  const resolveSettings$16 = (settings) => {
36815
36370
  const reactDoctor = settings?.["react-doctor"];
36816
36371
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -37169,7 +36724,7 @@ const noMultiComp = defineRule({
37169
36724
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
37170
36725
  for (const component of flagged.slice(1)) context.report({
37171
36726
  node: component.reportNode,
37172
- message: MESSAGE$19
36727
+ message: MESSAGE$18
37173
36728
  });
37174
36729
  } };
37175
36730
  }
@@ -37382,7 +36937,7 @@ const resolveReducerFunction = (node, currentFilename) => {
37382
36937
  };
37383
36938
  //#endregion
37384
36939
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
37385
- const MESSAGE$18 = "This reducer changes state in place, so your update is silently skipped.";
36940
+ const MESSAGE$17 = "This reducer changes state in place, so your update is silently skipped.";
37386
36941
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
37387
36942
  "copyWithin",
37388
36943
  "fill",
@@ -37591,7 +37146,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
37591
37146
  reportedNodes.add(options.crossFileConsumerCallSite);
37592
37147
  context.report({
37593
37148
  node: options.crossFileConsumerCallSite,
37594
- message: `${MESSAGE$18} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
37149
+ message: `${MESSAGE$17} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
37595
37150
  });
37596
37151
  return;
37597
37152
  }
@@ -37600,7 +37155,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
37600
37155
  reportedNodes.add(mutation.node);
37601
37156
  context.report({
37602
37157
  node: mutation.node,
37603
- message: MESSAGE$18
37158
+ message: MESSAGE$17
37604
37159
  });
37605
37160
  }
37606
37161
  };
@@ -38007,7 +37562,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
38007
37562
  });
38008
37563
  //#endregion
38009
37564
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
38010
- const MESSAGE$17 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
37565
+ const MESSAGE$16 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
38011
37566
  const KEYBOARD_HANDLER_PROP_NAMES = [
38012
37567
  "onKeyDown",
38013
37568
  "onKeyUp",
@@ -38126,7 +37681,7 @@ const noNoninteractiveTabindex = defineRule({
38126
37681
  if (numeric === null) {
38127
37682
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
38128
37683
  node: tabIndex,
38129
- message: MESSAGE$17
37684
+ message: MESSAGE$16
38130
37685
  });
38131
37686
  return;
38132
37687
  }
@@ -38148,7 +37703,7 @@ const noNoninteractiveTabindex = defineRule({
38148
37703
  if (!roleAttribute) {
38149
37704
  context.report({
38150
37705
  node: tabIndex,
38151
- message: MESSAGE$17
37706
+ message: MESSAGE$16
38152
37707
  });
38153
37708
  return;
38154
37709
  }
@@ -38162,7 +37717,7 @@ const noNoninteractiveTabindex = defineRule({
38162
37717
  }
38163
37718
  context.report({
38164
37719
  node: tabIndex,
38165
- message: MESSAGE$17
37720
+ message: MESSAGE$16
38166
37721
  });
38167
37722
  } };
38168
37723
  }
@@ -39581,6 +39136,174 @@ const noPropCallbackInRender = defineRule({
39581
39136
  } })
39582
39137
  });
39583
39138
  //#endregion
39139
+ //#region src/plugin/utils/is-proven-react-class-component.ts
39140
+ const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
39141
+ const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
39142
+ const expression = stripParenExpression(node);
39143
+ if (isNodeOfType(expression, "MemberExpression")) {
39144
+ const propertyName = getStaticPropertyName(expression);
39145
+ const receiver = stripParenExpression(expression.object);
39146
+ return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
39147
+ }
39148
+ if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
39149
+ if (!isNodeOfType(expression, "Identifier")) return false;
39150
+ const symbol = scopes.symbolFor(expression);
39151
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
39152
+ visitedSymbolIds.add(symbol.id);
39153
+ if (isImportedFromReact(symbol)) {
39154
+ const importedName = getImportedName(symbol.declarationNode);
39155
+ return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
39156
+ }
39157
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
39158
+ return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
39159
+ };
39160
+ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39161
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
39162
+ visitedClassNodes.add(classNode);
39163
+ return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
39164
+ };
39165
+ //#endregion
39166
+ //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
39167
+ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
39168
+ if (!isFunctionLike$2(functionNode)) return false;
39169
+ let containsReactHookCall = false;
39170
+ walkAst(functionNode.body, (node) => {
39171
+ if (containsReactHookCall) return false;
39172
+ if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
39173
+ if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
39174
+ containsReactHookCall = true;
39175
+ return false;
39176
+ }
39177
+ });
39178
+ return containsReactHookCall;
39179
+ };
39180
+ //#endregion
39181
+ //#region src/plugin/utils/function-returns-props-children.ts
39182
+ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
39183
+ if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
39184
+ const firstParameter = stripParenExpression(functionNode.params[0]);
39185
+ const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
39186
+ const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
39187
+ const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
39188
+ if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
39189
+ for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
39190
+ const propertyValue = stripParenExpression(property.value);
39191
+ const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
39192
+ if (!isNodeOfType(childrenBinding, "Identifier")) continue;
39193
+ const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
39194
+ if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
39195
+ }
39196
+ }
39197
+ return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
39198
+ const candidate = stripParenExpression(expression);
39199
+ if (isNodeOfType(candidate, "Identifier")) {
39200
+ const symbol = scopes.symbolFor(candidate);
39201
+ return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
39202
+ }
39203
+ if (!isNodeOfType(candidate, "MemberExpression")) return false;
39204
+ if (getStaticPropertyName(candidate) !== "children") return false;
39205
+ const receiver = stripParenExpression(candidate.object);
39206
+ if (!isNodeOfType(receiver, "Identifier")) return false;
39207
+ const receiverSymbol = scopes.symbolFor(receiver);
39208
+ if (!receiverSymbol || !propsParameterSymbol) return false;
39209
+ return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
39210
+ }, controlFlow);
39211
+ };
39212
+ //#endregion
39213
+ //#region src/plugin/utils/function-returns-only-null.ts
39214
+ const isNullExpression = (expression) => {
39215
+ const candidate = stripParenExpression(expression);
39216
+ return isNodeOfType(candidate, "Literal") && candidate.value === null;
39217
+ };
39218
+ const functionReturnsOnlyNull = (functionNode) => {
39219
+ if (!isFunctionLike$2(functionNode)) return false;
39220
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
39221
+ const returnStatements = collectFunctionReturnStatements(functionNode);
39222
+ return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
39223
+ };
39224
+ //#endregion
39225
+ //#region src/plugin/utils/is-proven-styled-component-expression.ts
39226
+ const findFactoryRoot = (node) => {
39227
+ const candidate = stripParenExpression(node);
39228
+ if (isNodeOfType(candidate, "Identifier")) return candidate;
39229
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
39230
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
39231
+ return null;
39232
+ };
39233
+ const findFactoryPropertyName = (node) => {
39234
+ const candidate = stripParenExpression(node);
39235
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
39236
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
39237
+ return null;
39238
+ };
39239
+ const isProvenStyledComponentExpression = (expression, scopes) => {
39240
+ const candidate = stripParenExpression(expression);
39241
+ if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
39242
+ const factoryRoot = findFactoryRoot(candidate.tag);
39243
+ if (!factoryRoot) return false;
39244
+ const factoryPropertyName = findFactoryPropertyName(candidate.tag);
39245
+ if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
39246
+ const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
39247
+ if (!symbol || symbol.kind !== "import") return false;
39248
+ if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
39249
+ const importDeclaration = symbol.declarationNode.parent;
39250
+ return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
39251
+ };
39252
+ //#endregion
39253
+ //#region src/plugin/utils/is-proven-react-component-symbol.ts
39254
+ const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
39255
+ const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39256
+ const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39257
+ const candidate = stripParenExpression(expression);
39258
+ if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
39259
+ if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
39260
+ if (isProvenStyledComponentExpression(candidate, scopes)) return true;
39261
+ if (isNodeOfType(candidate, "Identifier")) {
39262
+ const symbol = scopes.symbolFor(candidate);
39263
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
39264
+ visitedSymbolIds.add(symbol.id);
39265
+ if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
39266
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
39267
+ return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
39268
+ }
39269
+ if (!isNodeOfType(candidate, "CallExpression")) return false;
39270
+ if (!hasStableCallTarget(candidate, scopes)) return false;
39271
+ if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
39272
+ const wrappedComponent = candidate.arguments[0];
39273
+ return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
39274
+ }
39275
+ if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
39276
+ const factory = candidate.arguments[0];
39277
+ if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
39278
+ const unwrappedFactory = stripParenExpression(factory);
39279
+ if (!isInlineFunctionExpression(unwrappedFactory)) return false;
39280
+ if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
39281
+ const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
39282
+ const returnedExpression = returnStatements[0]?.argument;
39283
+ return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
39284
+ };
39285
+ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
39286
+ const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
39287
+ for (const candidateSymbol of candidateSymbols) {
39288
+ if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
39289
+ if (isComponentDeclaration(candidateSymbol.declarationNode)) {
39290
+ if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
39291
+ continue;
39292
+ }
39293
+ const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
39294
+ if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
39295
+ if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
39296
+ continue;
39297
+ }
39298
+ if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
39299
+ if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
39300
+ continue;
39301
+ }
39302
+ if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
39303
+ }
39304
+ return false;
39305
+ };
39306
+ //#endregion
39584
39307
  //#region src/plugin/rules/architecture/no-prop-types.ts
39585
39308
  const PROP_TYPES_PROPERTY = "propTypes";
39586
39309
  const isPropTypesKey = (key, computed) => {
@@ -39788,7 +39511,7 @@ const noRandomKey = defineRule({
39788
39511
  });
39789
39512
  //#endregion
39790
39513
  //#region src/plugin/rules/react-builtins/no-react-children.ts
39791
- const MESSAGE$16 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
39514
+ const MESSAGE$15 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
39792
39515
  const isChildrenIdentifier = (node, contextNode) => {
39793
39516
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
39794
39517
  return isImportedFromModule(contextNode, "Children", "react");
@@ -39814,13 +39537,13 @@ const noReactChildren = defineRule({
39814
39537
  if (isChildrenIdentifier(memberObject, node)) {
39815
39538
  context.report({
39816
39539
  node: calleeOuter,
39817
- message: MESSAGE$16
39540
+ message: MESSAGE$15
39818
39541
  });
39819
39542
  return;
39820
39543
  }
39821
39544
  if (isReactNamespaceMember(memberObject, node)) context.report({
39822
39545
  node: calleeOuter,
39823
- message: MESSAGE$16
39546
+ message: MESSAGE$15
39824
39547
  });
39825
39548
  } })
39826
39549
  });
@@ -40501,7 +40224,7 @@ const noRenderPropChildren = defineRule({
40501
40224
  });
40502
40225
  //#endregion
40503
40226
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
40504
- const MESSAGE$15 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40227
+ const MESSAGE$14 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40505
40228
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
40506
40229
  const isUsedAsReturnValue = (parent) => {
40507
40230
  if (!parent) return false;
@@ -40519,7 +40242,7 @@ const noRenderReturnValue = defineRule({
40519
40242
  if (!isUsedAsReturnValue(node.parent)) return;
40520
40243
  context.report({
40521
40244
  node: node.callee,
40522
- message: MESSAGE$15
40245
+ message: MESSAGE$14
40523
40246
  });
40524
40247
  } })
40525
40248
  });
@@ -41328,7 +41051,7 @@ const noSelfUpdatingEffect = defineRule({
41328
41051
  });
41329
41052
  //#endregion
41330
41053
  //#region src/plugin/rules/react-builtins/no-set-state.ts
41331
- const MESSAGE$14 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41054
+ const MESSAGE$13 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41332
41055
  const noSetState = defineRule({
41333
41056
  id: "no-set-state",
41334
41057
  title: "Local class state forbidden",
@@ -41343,7 +41066,7 @@ const noSetState = defineRule({
41343
41066
  if (!getParentComponent(node)) return;
41344
41067
  context.report({
41345
41068
  node: node.callee,
41346
- message: MESSAGE$14
41069
+ message: MESSAGE$13
41347
41070
  });
41348
41071
  } })
41349
41072
  });
@@ -41713,7 +41436,7 @@ const isAbstractRole = (openingElement, settings) => {
41713
41436
  };
41714
41437
  //#endregion
41715
41438
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
41716
- const MESSAGE$13 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
41439
+ const MESSAGE$12 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
41717
41440
  const DEFAULT_HANDLERS = [
41718
41441
  "onClick",
41719
41442
  "onMouseDown",
@@ -41828,7 +41551,7 @@ const noStaticElementInteractions = defineRule({
41828
41551
  if (!roleAttribute || !roleAttribute.value) {
41829
41552
  context.report({
41830
41553
  node: node.name,
41831
- message: MESSAGE$13
41554
+ message: MESSAGE$12
41832
41555
  });
41833
41556
  return;
41834
41557
  }
@@ -41838,7 +41561,7 @@ const noStaticElementInteractions = defineRule({
41838
41561
  if (isRecognizedRoleString(attributeValue.value)) return;
41839
41562
  context.report({
41840
41563
  node: node.name,
41841
- message: MESSAGE$13
41564
+ message: MESSAGE$12
41842
41565
  });
41843
41566
  return;
41844
41567
  }
@@ -41846,7 +41569,7 @@ const noStaticElementInteractions = defineRule({
41846
41569
  if (!settings.allowExpressionValues) {
41847
41570
  context.report({
41848
41571
  node: node.name,
41849
- message: MESSAGE$13
41572
+ message: MESSAGE$12
41850
41573
  });
41851
41574
  return;
41852
41575
  }
@@ -41854,7 +41577,7 @@ const noStaticElementInteractions = defineRule({
41854
41577
  if (isStaticNullishExpression(expression)) {
41855
41578
  context.report({
41856
41579
  node: node.name,
41857
- message: MESSAGE$13
41580
+ message: MESSAGE$12
41858
41581
  });
41859
41582
  return;
41860
41583
  }
@@ -41864,7 +41587,7 @@ const noStaticElementInteractions = defineRule({
41864
41587
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
41865
41588
  context.report({
41866
41589
  node: node.name,
41867
- message: MESSAGE$13
41590
+ message: MESSAGE$12
41868
41591
  });
41869
41592
  return;
41870
41593
  }
@@ -41872,7 +41595,7 @@ const noStaticElementInteractions = defineRule({
41872
41595
  }
41873
41596
  context.report({
41874
41597
  node: node.name,
41875
- message: MESSAGE$13
41598
+ message: MESSAGE$12
41876
41599
  });
41877
41600
  } };
41878
41601
  }
@@ -41978,7 +41701,7 @@ const noStringRefs = defineRule({
41978
41701
  });
41979
41702
  //#endregion
41980
41703
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
41981
- const MESSAGE$12 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
41704
+ const MESSAGE$11 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
41982
41705
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
41983
41706
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
41984
41707
  const noSyncXhr = defineRule({
@@ -41999,7 +41722,7 @@ const noSyncXhr = defineRule({
41999
41722
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
42000
41723
  context.report({
42001
41724
  node,
42002
- message: MESSAGE$12
41725
+ message: MESSAGE$11
42003
41726
  });
42004
41727
  };
42005
41728
  return {
@@ -42024,7 +41747,7 @@ const noSyncXhr = defineRule({
42024
41747
  });
42025
41748
  //#endregion
42026
41749
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
42027
- const MESSAGE$11 = "This value is `undefined` because function components have no `this`.";
41750
+ const MESSAGE$10 = "This value is `undefined` because function components have no `this`.";
42028
41751
  const isInsideClassMethod = (node, customClassFactoryNames) => {
42029
41752
  let ancestor = node.parent;
42030
41753
  while (ancestor) {
@@ -42112,7 +41835,7 @@ const noThisInSfc = defineRule({
42112
41835
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
42113
41836
  context.report({
42114
41837
  node,
42115
- message: MESSAGE$11
41838
+ message: MESSAGE$10
42116
41839
  });
42117
41840
  } };
42118
41841
  }
@@ -43757,11 +43480,7 @@ const resolveSettings$8 = (settings) => {
43757
43480
  propNamePattern: ruleSettings.propNamePattern ?? "render*"
43758
43481
  };
43759
43482
  };
43760
- const isReactCreateElementCall = (node, scopes) => isReactApiCall(node, "createElement", scopes, {
43761
- allowGlobalReactNamespace: true,
43762
- resolveNamedAliases: true
43763
- });
43764
- const expressionContainsJsxOrCreateElement = (root, scopes) => {
43483
+ const expressionContainsJsxOrCreateElement = (root) => {
43765
43484
  let found = false;
43766
43485
  walkAst(root, (node) => {
43767
43486
  if (found) return false;
@@ -43770,17 +43489,17 @@ const expressionContainsJsxOrCreateElement = (root, scopes) => {
43770
43489
  found = true;
43771
43490
  return false;
43772
43491
  }
43773
- if (isNodeOfType(node, "CallExpression") && isReactCreateElementCall(node, scopes)) {
43492
+ if (isNodeOfType(node, "CallExpression") && isCreateElementCall(node)) {
43774
43493
  found = true;
43775
43494
  return false;
43776
43495
  }
43777
43496
  });
43778
43497
  return found;
43779
43498
  };
43780
- const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => expressionContainsJsxOrCreateElement(expression, scopes), controlFlow);
43781
- const isReactClassComponent = (classNode, scopes) => {
43499
+ const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement, controlFlow);
43500
+ const isReactClassComponent = (classNode) => {
43782
43501
  if (isEs6Component(classNode)) return true;
43783
- return expressionContainsJsxOrCreateElement(classNode, scopes);
43502
+ return expressionContainsJsxOrCreateElement(classNode);
43784
43503
  };
43785
43504
  const findEnclosingComponent = (node, scopes, controlFlow) => {
43786
43505
  let walker = node.parent;
@@ -43797,7 +43516,7 @@ const findEnclosingComponent = (node, scopes, controlFlow) => {
43797
43516
  };
43798
43517
  }
43799
43518
  if (isNodeOfType(walker, "ClassDeclaration") || isNodeOfType(walker, "ClassExpression")) {
43800
- if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker, scopes)) return {
43519
+ if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker)) return {
43801
43520
  component: walker,
43802
43521
  name: walker.id.name
43803
43522
  };
@@ -43886,12 +43605,12 @@ const isObjectCallbackCandidate = (node) => {
43886
43605
  }
43887
43606
  return false;
43888
43607
  };
43889
- const hocCallContainsComponent = (call, scopes) => {
43608
+ const hocCallContainsComponent = (call) => {
43890
43609
  const firstArgument = call.arguments[0];
43891
43610
  if (!firstArgument) return false;
43892
- if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument, scopes);
43893
- if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument, scopes);
43894
- return expressionContainsJsxOrCreateElement(firstArgument, scopes);
43611
+ if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument);
43612
+ if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument);
43613
+ return expressionContainsJsxOrCreateElement(firstArgument);
43895
43614
  };
43896
43615
  const isFirstArgumentOfHocCall = (node) => {
43897
43616
  const parent = node.parent;
@@ -43924,35 +43643,19 @@ const isReturnOfMapCallback = (node) => {
43924
43643
  }
43925
43644
  return false;
43926
43645
  };
43646
+ const isCloneElementCall = (node) => {
43647
+ if (!isNodeOfType(node, "CallExpression")) return false;
43648
+ const callee = node.callee;
43649
+ if (isNodeOfType(callee, "Identifier")) return callee.name === "cloneElement";
43650
+ return isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "cloneElement";
43651
+ };
43927
43652
  const TS_VALUE_PASSTHROUGH_TYPES = new Set([
43928
43653
  "TSAsExpression",
43929
43654
  "TSNonNullExpression",
43930
43655
  "TSSatisfiesExpression",
43931
43656
  "TSTypeAssertion"
43932
43657
  ]);
43933
- const ELEMENT_TYPE_PROP_NAMES = new Set([
43934
- "as",
43935
- "body",
43936
- "calendarcontainer",
43937
- "component",
43938
- "fallback",
43939
- "tooltip"
43940
- ]);
43941
- const isElementTypeJsxAttribute = (node) => {
43942
- if (!isNodeOfType(node, "JSXAttribute")) return false;
43943
- if (!isNodeOfType(node.name, "JSXIdentifier")) return false;
43944
- const attributeName = node.name.name;
43945
- return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
43946
- };
43947
- const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
43948
- allowGlobalReactNamespace: true,
43949
- resolveNamedAliases: true
43950
- });
43951
- const isReactLazyCall = (call, scopes) => isReactApiCall(call, "lazy", scopes, {
43952
- allowGlobalReactNamespace: true,
43953
- resolveNamedAliases: true
43954
- });
43955
- const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
43658
+ const isRenderFlowingReadReference = (identifier) => {
43956
43659
  let valueNode = identifier;
43957
43660
  let parent = valueNode.parent;
43958
43661
  while (parent) {
@@ -43962,26 +43665,20 @@ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @_
43962
43665
  continue;
43963
43666
  }
43964
43667
  switch (parent.type) {
43965
- case "JSXOpeningElement": return parent.name === valueNode;
43966
- case "JSXExpressionContainer": return Boolean(parent.parent && isElementTypeJsxAttribute(parent.parent));
43967
- case "ReturnStatement": return false;
43968
- case "ArrowFunctionExpression": return false;
43668
+ case "JSXExpressionContainer":
43669
+ case "ReturnStatement": return true;
43670
+ case "ArrowFunctionExpression": return parent.body === valueNode;
43969
43671
  case "CallExpression":
43970
43672
  if (parent.callee === valueNode) return false;
43971
- if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
43972
- if (isReactCreateElementCall(parent, scopes)) return true;
43673
+ if (isCreateElementCall(parent) || isCloneElementCall(parent)) return true;
43973
43674
  valueNode = parent;
43974
43675
  parent = parent.parent;
43975
43676
  continue;
43976
- case "VariableDeclarator": {
43977
- if (parent.init !== valueNode || !isNodeOfType(parent.id, "Identifier")) return false;
43978
- const aliasSymbol = scopes.symbolFor(parent.id);
43979
- if (!aliasSymbol || aliasSymbol.kind !== "const" || visitedSymbols.has(aliasSymbol.id)) return false;
43980
- const nextVisitedSymbols = new Set(visitedSymbols);
43981
- nextVisitedSymbols.add(aliasSymbol.id);
43982
- return aliasSymbol.references.some((reference) => reference.flag === "read" && isRenderFlowingReadReference(reference.identifier, scopes, nextVisitedSymbols));
43983
- }
43984
- case "AssignmentExpression": return false;
43677
+ case "VariableDeclarator": return parent.init === valueNode && isNodeOfType(parent.id, "Identifier") && isReactComponentName(parent.id.name);
43678
+ case "AssignmentExpression": {
43679
+ const assignmentTarget = parent.left;
43680
+ return parent.right === valueNode && isNodeOfType(assignmentTarget, "Identifier") && isReactComponentName(assignmentTarget.name);
43681
+ }
43985
43682
  case "Property":
43986
43683
  if (parent.value !== valueNode) return false;
43987
43684
  valueNode = parent;
@@ -44019,7 +43716,6 @@ const noUnstableNestedComponents = defineRule({
44019
43716
  severity: "warn",
44020
43717
  recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
44021
43718
  category: "Performance",
44022
- tags: ["react-jsx-only"],
44023
43719
  create: (context) => {
44024
43720
  const settings = resolveSettings$8(context.settings);
44025
43721
  const renderPropRegex = compileGlob(settings.propNamePattern);
@@ -44082,7 +43778,7 @@ const noUnstableNestedComponents = defineRule({
44082
43778
  },
44083
43779
  Identifier(node) {
44084
43780
  if (!isReactComponentName(node.name)) return;
44085
- if (!isRenderFlowingReadReference(node, context.scopes)) return;
43781
+ if (!isRenderFlowingReadReference(node)) return;
44086
43782
  recordInstantiation(node, node.name);
44087
43783
  },
44088
43784
  FunctionDeclaration: checkFunctionLike,
@@ -44091,33 +43787,28 @@ const noUnstableNestedComponents = defineRule({
44091
43787
  ClassDeclaration(node) {
44092
43788
  if (!node.id) return;
44093
43789
  if (!isReactComponentName(node.id.name)) return;
44094
- if (!isReactClassComponent(node, context.scopes)) return;
43790
+ if (!isReactClassComponent(node)) return;
44095
43791
  enqueueCandidate(node, null);
44096
43792
  },
44097
43793
  ClassExpression(node) {
44098
43794
  const inferredName = node.id?.name ?? inferFunctionLikeName(node);
44099
43795
  if (!inferredName || !isReactComponentName(inferredName)) return;
44100
- if (!isReactClassComponent(node, context.scopes)) return;
43796
+ if (!isReactClassComponent(node)) return;
44101
43797
  enqueueCandidate(node, null);
44102
43798
  },
44103
43799
  CallExpression(node) {
44104
- if (isReactCreateElementCall(node, context.scopes)) {
43800
+ if (isCreateElementCall(node)) {
44105
43801
  const firstArgument = node.arguments[0];
44106
43802
  if (firstArgument && isNodeOfType(firstArgument, "Identifier")) recordInstantiation(firstArgument, firstArgument.name);
44107
43803
  else if (firstArgument && isNodeOfType(firstArgument, "MemberExpression")) recordMemberChainInstantiation(firstArgument);
44108
43804
  }
44109
- const isReactLazy = isReactLazyCall(node, context.scopes);
44110
- if (!isReactLazy && !isHocCallee$1(node)) return;
44111
- if (!isReactLazy && !hocCallContainsComponent(node, context.scopes)) return;
44112
- const inferredName = inferFunctionLikeName(node);
44113
- const propInfo = isComponentDeclaredInProp(node);
44114
- if (propInfo === null && (!inferredName || !isReactComponentName(inferredName))) return;
44115
- enqueueCandidate(node, propInfo === null ? inferredName : null);
43805
+ if (!isHocCallee$1(node)) return;
43806
+ if (!hocCallContainsComponent(node)) return;
43807
+ enqueueCandidate(node, null);
44116
43808
  },
44117
43809
  "Program:exit"() {
44118
43810
  for (const report of queuedReports) {
44119
43811
  if (report.requiredInstantiationName !== null) {
44120
- if ((report.requiredInstantiationBinding ? context.scopes.symbolFor(report.requiredInstantiationBinding) : null)?.references.some((reference) => reference.flag === "write" || reference.flag === "read-write")) continue;
44121
43812
  if (!(report.requiredInstantiationBinding !== null ? instantiatedBindingIdentifiers.has(report.requiredInstantiationBinding) : instantiatedComponentNames.has(report.requiredInstantiationName))) continue;
44122
43813
  }
44123
43814
  context.report({
@@ -44291,7 +43982,7 @@ const noWideLetterSpacing = defineRule({
44291
43982
  //#endregion
44292
43983
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
44293
43984
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
44294
- const MESSAGE$10 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
43985
+ const MESSAGE$9 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
44295
43986
  const resolveSettings$7 = (settings) => {
44296
43987
  const reactDoctor = settings?.["react-doctor"];
44297
43988
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -44325,7 +44016,7 @@ const noWillUpdateSetState = defineRule({
44325
44016
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
44326
44017
  context.report({
44327
44018
  node: node.callee,
44328
- message: MESSAGE$10
44019
+ message: MESSAGE$9
44329
44020
  });
44330
44021
  } };
44331
44022
  }
@@ -45321,7 +45012,7 @@ const preactNoRenderArguments = defineRule({
45321
45012
  });
45322
45013
  //#endregion
45323
45014
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
45324
- const MESSAGE$9 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
45015
+ const MESSAGE$8 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
45325
45016
  const preactPreferOndblclick = defineRule({
45326
45017
  id: "preact-prefer-ondblclick",
45327
45018
  title: "onDoubleClick instead of onDblClick",
@@ -45336,7 +45027,7 @@ const preactPreferOndblclick = defineRule({
45336
45027
  if (!onDoubleClickAttribute) return;
45337
45028
  context.report({
45338
45029
  node: onDoubleClickAttribute,
45339
- message: MESSAGE$9
45030
+ message: MESSAGE$8
45340
45031
  });
45341
45032
  } })
45342
45033
  });
@@ -45587,7 +45278,7 @@ const preferExplicitVariants = defineRule({
45587
45278
  });
45588
45279
  //#endregion
45589
45280
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
45590
- const MESSAGE$8 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
45281
+ const MESSAGE$7 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
45591
45282
  const resolveSettings$4 = (settings) => {
45592
45283
  const reactDoctor = settings?.["react-doctor"];
45593
45284
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -45626,7 +45317,7 @@ const preferFunctionComponent = defineRule({
45626
45317
  const reportNode = node.id ?? node;
45627
45318
  context.report({
45628
45319
  node: reportNode,
45629
- message: MESSAGE$8
45320
+ message: MESSAGE$7
45630
45321
  });
45631
45322
  };
45632
45323
  return {
@@ -47204,258 +46895,7 @@ const queryMutationMissingInvalidation = defineRule({
47204
46895
  }
47205
46896
  });
47206
46897
  //#endregion
47207
- //#region src/plugin/utils/is-node-conditionally-executed.ts
47208
- const isNodeConditionallyExecuted = (node, boundary) => {
47209
- let child = node;
47210
- let parent = child.parent ?? null;
47211
- while (parent && parent !== boundary) {
47212
- if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === child || parent.alternate === child)) return true;
47213
- if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) return true;
47214
- if (isNodeOfType(parent, "AssignmentPattern") && parent.right === child) return true;
47215
- child = parent;
47216
- parent = child.parent ?? null;
47217
- }
47218
- return false;
47219
- };
47220
- //#endregion
47221
- //#region src/plugin/rules/tanstack-query/utils/resolve-tanstack-query-hook-name.ts
47222
- const resolveTanstackNamespaceHookName = (memberExpression, contextNode, scopes) => {
47223
- const hookName = getStaticPropertyKeyName(memberExpression, { allowComputedString: true });
47224
- const namespaceObject = stripParenExpression(memberExpression.object);
47225
- if (!hookName || !TANSTACK_QUERY_HOOKS.has(hookName)) return null;
47226
- if (!isNodeOfType(namespaceObject, "Identifier")) return null;
47227
- const resolvedNamespaceSymbol = resolveConstIdentifierAlias(namespaceObject, scopes);
47228
- if (resolvedNamespaceSymbol?.kind !== "import") return null;
47229
- const namespaceBinding = getImportBindingForName(contextNode, resolvedNamespaceSymbol.name);
47230
- return namespaceBinding?.isNamespace && isTanstackQuerySource(namespaceBinding.source) ? hookName : null;
47231
- };
47232
- const resolveTanstackQueryHookName = (callExpression, scopes) => {
47233
- const callee = stripParenExpression(callExpression.callee);
47234
- if (isNodeOfType(callee, "Identifier")) {
47235
- const resolvedSymbol = resolveConstIdentifierAlias(callee, scopes);
47236
- if (!resolvedSymbol) return null;
47237
- if (resolvedSymbol.kind === "const" && resolvedSymbol.initializer) {
47238
- const initializer = stripParenExpression(resolvedSymbol.initializer);
47239
- return isNodeOfType(initializer, "MemberExpression") ? resolveTanstackNamespaceHookName(initializer, callExpression, scopes) : null;
47240
- }
47241
- if (resolvedSymbol.kind !== "import") return null;
47242
- const importBinding = getImportBindingForName(callExpression, resolvedSymbol.name);
47243
- if (importBinding === null) return null;
47244
- if (importBinding.isNamespace || !isTanstackQuerySource(importBinding.source)) return null;
47245
- return importBinding.exportedName !== null && TANSTACK_QUERY_HOOKS.has(importBinding.exportedName) ? importBinding.exportedName : null;
47246
- }
47247
- if (isNodeOfType(callee, "MemberExpression")) return resolveTanstackNamespaceHookName(callee, callExpression, scopes);
47248
- return null;
47249
- };
47250
- const resolveTanstackQueryHookNameFromInitializer = (initializer, scopes) => {
47251
- const unwrappedInitializer = stripParenExpression(initializer);
47252
- if (isNodeOfType(unwrappedInitializer, "CallExpression")) return resolveTanstackQueryHookName(unwrappedInitializer, scopes);
47253
- if (!isNodeOfType(unwrappedInitializer, "Identifier")) return null;
47254
- const resolvedSymbol = resolveConstIdentifierAlias(unwrappedInitializer, scopes);
47255
- if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
47256
- const resolvedInitializer = stripParenExpression(resolvedSymbol.initializer);
47257
- if (!isNodeOfType(resolvedInitializer, "CallExpression")) return null;
47258
- return resolveTanstackQueryHookName(resolvedInitializer, scopes);
47259
- };
47260
- //#endregion
47261
46898
  //#region src/plugin/rules/tanstack-query/query-no-query-in-effect.ts
47262
- const isTanstackQueryResult = (expression, context) => Boolean(resolveTanstackQueryHookNameFromInitializer(expression, context.scopes));
47263
- const isStaticRefetchMember = (memberExpression) => getStaticPropertyKeyName(memberExpression, { allowComputedString: true }) === "refetch";
47264
- const resolveCalledFunction = (callee, context) => {
47265
- const unwrappedCallee = stripParenExpression(callee);
47266
- if (isFunctionLike$2(unwrappedCallee)) return unwrappedCallee;
47267
- if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
47268
- const symbol = resolveConstIdentifierAlias(unwrappedCallee, context.scopes);
47269
- if (!symbol) return null;
47270
- const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
47271
- return candidate && isFunctionLike$2(candidate) ? candidate : null;
47272
- };
47273
- const hasSuspensionBefore = (functionNode, boundary, context) => {
47274
- if (!isFunctionLike$2(functionNode)) return true;
47275
- if (functionNode.generator) return true;
47276
- const boundaryStart = getRangeStart(boundary);
47277
- if (boundaryStart === null) return true;
47278
- let hasSuspension = false;
47279
- walkAst(functionNode, (node) => {
47280
- if (node !== functionNode && isFunctionLike$2(node)) return false;
47281
- if (!isNodeOfType(node, "AwaitExpression") || !isNodeReachableWithinFunction(node, context)) return;
47282
- const suspensionStart = getRangeStart(node);
47283
- if (suspensionStart !== null && suspensionStart < boundaryStart) {
47284
- hasSuspension = true;
47285
- return false;
47286
- }
47287
- });
47288
- return hasSuspension;
47289
- };
47290
- const isFunctionAncestor = (ancestor, functionNode) => {
47291
- let enclosingFunction = findEnclosingFunction(functionNode);
47292
- while (enclosingFunction) {
47293
- if (enclosingFunction === ancestor) return true;
47294
- enclosingFunction = findEnclosingFunction(enclosingFunction);
47295
- }
47296
- return false;
47297
- };
47298
- const isUnconditionallyExecuted = (node, functionNode, context) => context.cfg.isUnconditionalFromEntry(node) && !isNodeConditionallyExecuted(node, functionNode) && !(isNodeOfType(node, "MemberExpression") && isNodeOfType(node.parent, "AssignmentExpression") && node.parent.left === node && (node.parent.operator === "&&=" || node.parent.operator === "||=" || node.parent.operator === "??="));
47299
- const functionInvokesTarget = (callerFunction, targetFunction, context, visitedFunctions, canCrossSuspension = false) => {
47300
- if (visitedFunctions.has(callerFunction)) return false;
47301
- visitedFunctions.add(callerFunction);
47302
- let invokesTarget = false;
47303
- walkAst(callerFunction, (node) => {
47304
- if (node !== callerFunction && isFunctionLike$2(node)) return false;
47305
- if (!isNodeOfType(node, "CallExpression")) return;
47306
- if (!isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context) || !canCrossSuspension && hasSuspensionBefore(callerFunction, node, context)) return;
47307
- const calledFunction = resolveCalledFunction(node.callee, context);
47308
- if (calledFunction === targetFunction || calledFunction && functionInvokesTarget(calledFunction, targetFunction, context, visitedFunctions, canCrossSuspension)) {
47309
- invokesTarget = true;
47310
- return false;
47311
- }
47312
- });
47313
- return invokesTarget;
47314
- };
47315
- const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
47316
- const boundaryFunction = findEnclosingFunction(boundary);
47317
- const boundaryStart = getRangeStart(boundary);
47318
- if (!boundaryFunction || boundaryStart === null) return false;
47319
- let isInvokedBefore = false;
47320
- walkAst(boundaryFunction, (node) => {
47321
- if (node !== boundaryFunction && isFunctionLike$2(node)) return false;
47322
- if (!isNodeOfType(node, "CallExpression")) return;
47323
- const callStart = getRangeStart(node);
47324
- if (callStart === null || callStart >= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, boundaryFunction, context) || hasSuspensionBefore(boundaryFunction, node, context)) return;
47325
- const calledFunction = resolveCalledFunction(node.callee, context);
47326
- if (calledFunction === invokedFunction || calledFunction && functionInvokesTarget(calledFunction, invokedFunction, context, /* @__PURE__ */ new Set())) {
47327
- isInvokedBefore = true;
47328
- return false;
47329
- }
47330
- });
47331
- return isInvokedBefore;
47332
- };
47333
- const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, context) => {
47334
- const boundaryStart = getRangeStart(boundary);
47335
- if (boundaryStart === null) return false;
47336
- let isInvokedAfter = false;
47337
- walkAst(callerFunction, (node) => {
47338
- if (node !== callerFunction && isFunctionLike$2(node)) return false;
47339
- if (!isNodeOfType(node, "CallExpression")) return;
47340
- const callStart = getRangeStart(node);
47341
- if (callStart === null || callStart <= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context)) return;
47342
- const calledFunction = resolveCalledFunction(node.callee, context);
47343
- if (calledFunction === invokedFunction || calledFunction && functionInvokesTarget(calledFunction, invokedFunction, context, /* @__PURE__ */ new Set(), true)) {
47344
- isInvokedAfter = true;
47345
- return false;
47346
- }
47347
- });
47348
- return isInvokedAfter;
47349
- };
47350
- const isWriteExecutedBefore = (writeNode, boundary, context, deferredExecutionFunction) => {
47351
- const writeStart = getRangeStart(writeNode);
47352
- const boundaryStart = getRangeStart(boundary);
47353
- const writeFunction = findEnclosingFunction(writeNode);
47354
- const writeExecutionBoundary = writeFunction ?? findProgramRoot(writeNode);
47355
- if (writeStart === null || boundaryStart === null || !writeExecutionBoundary || !isNodeReachableWithinFunction(writeNode, context) || !isUnconditionallyExecuted(writeNode, writeExecutionBoundary, context)) return false;
47356
- const boundaryFunction = findEnclosingFunction(boundary);
47357
- const renderFunction = deferredExecutionFunction ? findEnclosingFunction(deferredExecutionFunction) : null;
47358
- if (writeFunction === boundaryFunction) return writeStart < boundaryStart;
47359
- if (!writeFunction) return writeStart < boundaryStart;
47360
- if (boundaryFunction && isFunctionAncestor(writeFunction, boundaryFunction)) {
47361
- if (Boolean(renderFunction && (writeFunction === renderFunction || isFunctionAncestor(writeFunction, renderFunction)))) return true;
47362
- if (deferredExecutionFunction && boundaryFunction !== deferredExecutionFunction) {
47363
- if (hasSuspensionBefore(boundaryFunction, boundary, context) && isFunctionInvokedBefore(boundaryFunction, writeNode, context)) return true;
47364
- return isFunctionInvokedAfter(boundaryFunction, writeNode, writeFunction, context);
47365
- }
47366
- return writeStart < boundaryStart;
47367
- }
47368
- if (renderFunction && isFunctionAncestor(renderFunction, writeFunction) && !hasSuspensionBefore(writeFunction, writeNode, context) && functionInvokesTarget(renderFunction, writeFunction, context, /* @__PURE__ */ new Set())) return true;
47369
- return !hasSuspensionBefore(writeFunction, writeNode, context) && isFunctionInvokedBefore(writeFunction, boundary, context);
47370
- };
47371
- const getStaticStringValue = (node) => {
47372
- const unwrappedNode = stripParenExpression(node);
47373
- if (isNodeOfType(unwrappedNode, "Literal") && typeof unwrappedNode.value === "string") return unwrappedNode.value;
47374
- if (isNodeOfType(unwrappedNode, "TemplateLiteral") && unwrappedNode.expressions.length === 0) return getStaticTemplateLiteralValue(unwrappedNode);
47375
- return null;
47376
- };
47377
- const isSameRefetchMember = (target, candidate, context) => {
47378
- const unwrappedTarget = stripParenExpression(target);
47379
- const unwrappedCandidate = stripParenExpression(candidate);
47380
- if (!isNodeOfType(unwrappedTarget, "Identifier") || !isNodeOfType(unwrappedCandidate, "MemberExpression") || !isStaticRefetchMember(unwrappedCandidate)) return false;
47381
- const candidateTarget = stripParenExpression(unwrappedCandidate.object);
47382
- if (!isNodeOfType(candidateTarget, "Identifier")) return false;
47383
- const targetSymbol = resolveConstIdentifierAlias(unwrappedTarget, context.scopes);
47384
- const candidateSymbol = resolveConstIdentifierAlias(candidateTarget, context.scopes);
47385
- return Boolean(targetSymbol && candidateSymbol?.id === targetSymbol.id);
47386
- };
47387
- const getRefetchMutationTarget = (node, context) => {
47388
- if (isNodeOfType(node, "MemberExpression") && isStaticRefetchMember(node)) {
47389
- const parent = node.parent;
47390
- if (isNodeOfType(parent, "AssignmentExpression") && parent.left === node && isSameRefetchMember(node.object, parent.right, context)) return null;
47391
- return isNodeOfType(parent, "AssignmentExpression") && parent.left === node || isNodeOfType(parent, "UpdateExpression") && parent.argument === node || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" ? node.object : null;
47392
- }
47393
- if (!isNodeOfType(node, "CallExpression")) return null;
47394
- const callee = stripParenExpression(node.callee);
47395
- if (!isNodeOfType(callee, "MemberExpression")) return null;
47396
- const receiver = stripParenExpression(callee.object);
47397
- if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || context.scopes.symbolFor(receiver)) return null;
47398
- const methodName = getStaticPropertyKeyName(callee, { allowComputedString: true });
47399
- const target = node.arguments[0];
47400
- if (!target || isNodeOfType(target, "SpreadElement")) return null;
47401
- if (methodName === "defineProperty") {
47402
- const propertyKey = node.arguments[1];
47403
- if (!propertyKey || getStaticStringValue(propertyKey) !== "refetch") return null;
47404
- const descriptor = node.arguments[2];
47405
- if (isNodeOfType(descriptor, "ObjectExpression")) {
47406
- const valueProperty = descriptor.properties.find((property) => isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "value");
47407
- if (isNodeOfType(valueProperty, "Property") && isSameRefetchMember(target, valueProperty.value, context)) return null;
47408
- }
47409
- return target;
47410
- }
47411
- if (methodName !== "assign") return null;
47412
- let finalRefetchValue = null;
47413
- for (const source of node.arguments.slice(1)) {
47414
- if (!isNodeOfType(source, "ObjectExpression")) continue;
47415
- for (const property of source.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "refetch") finalRefetchValue = property.value;
47416
- }
47417
- if (!finalRefetchValue || isSameRefetchMember(target, finalRefetchValue, context)) return null;
47418
- return target;
47419
- };
47420
- const hasRefetchMemberWriteBefore = (expression, boundary, context, deferredExecutionFunction) => {
47421
- const unwrappedExpression = stripParenExpression(expression);
47422
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return false;
47423
- const resultSymbol = resolveConstIdentifierAlias(unwrappedExpression, context.scopes);
47424
- if (!resultSymbol) return false;
47425
- const program = findProgramRoot(expression);
47426
- if (!program) return true;
47427
- if (getRangeStart(boundary) === null) return true;
47428
- let hasWrite = false;
47429
- walkAst(program, (node) => {
47430
- const mutationTarget = getRefetchMutationTarget(node, context);
47431
- if (!mutationTarget) return;
47432
- if (!isWriteExecutedBefore(node, boundary, context, deferredExecutionFunction)) return;
47433
- const object = stripParenExpression(mutationTarget);
47434
- if (!isNodeOfType(object, "Identifier")) return;
47435
- if (resolveConstIdentifierAlias(object, context.scopes)?.id === resultSymbol.id) {
47436
- hasWrite = true;
47437
- return false;
47438
- }
47439
- });
47440
- return hasWrite;
47441
- };
47442
- const isTanstackRefetchExpression = (expression, context, deferredExecutionFunction, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
47443
- const unwrappedExpression = stripParenExpression(expression);
47444
- if (isNodeOfType(unwrappedExpression, "MemberExpression")) return isStaticRefetchMember(unwrappedExpression) && isTanstackQueryResult(unwrappedExpression.object, context) && !hasRefetchMemberWriteBefore(unwrappedExpression.object, unwrappedExpression, context, deferredExecutionFunction);
47445
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return false;
47446
- const symbol = context.scopes.symbolFor(unwrappedExpression);
47447
- if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read") || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
47448
- visitedSymbolIds.add(symbol.id);
47449
- const bindingProperty = symbol.bindingIdentifier.parent;
47450
- if (isNodeOfType(bindingProperty, "Property") && getStaticPropertyKeyName(bindingProperty, { allowComputedString: true }) === "refetch") {
47451
- const initializer = symbol.declarationNode.init;
47452
- return Boolean(initializer && isTanstackQueryResult(initializer, context) && !hasRefetchMemberWriteBefore(initializer, symbol.declarationNode, context, null));
47453
- }
47454
- return Boolean(symbol.declarationNode.id === symbol.bindingIdentifier && symbol.initializer && isTanstackRefetchExpression(symbol.initializer, context, null, visitedSymbolIds));
47455
- };
47456
- const isTanstackRefetchCall = (callExpression, context, effectCallback) => {
47457
- return isTanstackRefetchExpression(callExpression.callee, context, effectCallback);
47458
- };
47459
46899
  const queryNoQueryInEffect = defineRule({
47460
46900
  id: "query-no-query-in-effect",
47461
46901
  title: "Query refetch inside useEffect",
@@ -47471,7 +46911,8 @@ const queryNoQueryInEffect = defineRule({
47471
46911
  walkAst(callback, (child) => {
47472
46912
  if (child !== callback && isFunctionLike$2(child) && !effectInvokedFunctions.has(child)) return false;
47473
46913
  if (!isNodeOfType(child, "CallExpression")) return;
47474
- if (isTanstackRefetchCall(child, context, callback)) context.report({
46914
+ const callee = child.callee;
46915
+ if (isNodeOfType(callee, "Identifier") && callee.name === "refetch" || isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === "refetch") context.report({
47475
46916
  node: child,
47476
46917
  message: "refetch() inside useEffect duplicates work React Query already does, causing extra fetches."
47477
46918
  });
@@ -47480,6 +46921,28 @@ const queryNoQueryInEffect = defineRule({
47480
46921
  });
47481
46922
  //#endregion
47482
46923
  //#region src/plugin/rules/tanstack-query/query-no-rest-destructuring.ts
46924
+ const resolveTanstackQueryHookName = (callExpression) => {
46925
+ const callee = callExpression.callee;
46926
+ if (isNodeOfType(callee, "Identifier")) {
46927
+ const importBinding = getImportBindingForName(callExpression, callee.name);
46928
+ if (importBinding === null) return TANSTACK_QUERY_HOOKS.has(callee.name) ? callee.name : null;
46929
+ if (importBinding.isNamespace || !isTanstackQuerySource(importBinding.source)) return null;
46930
+ return importBinding.exportedName !== null && TANSTACK_QUERY_HOOKS.has(importBinding.exportedName) ? importBinding.exportedName : null;
46931
+ }
46932
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && TANSTACK_QUERY_HOOKS.has(callee.property.name) && isNodeOfType(callee.object, "Identifier")) {
46933
+ const namespaceBinding = getImportBindingForName(callExpression, callee.object.name);
46934
+ if (namespaceBinding?.isNamespace && isTanstackQuerySource(namespaceBinding.source)) return callee.property.name;
46935
+ }
46936
+ return null;
46937
+ };
46938
+ const resolveHookNameFromInitializer = (initializer, scopes) => {
46939
+ if (isNodeOfType(initializer, "CallExpression")) return resolveTanstackQueryHookName(initializer);
46940
+ if (!isNodeOfType(initializer, "Identifier")) return null;
46941
+ const resolvedSymbol = resolveConstIdentifierAlias(initializer, scopes);
46942
+ if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
46943
+ if (!isNodeOfType(resolvedSymbol.initializer, "CallExpression")) return null;
46944
+ return resolveTanstackQueryHookName(resolvedSymbol.initializer);
46945
+ };
47483
46946
  const queryNoRestDestructuring = defineRule({
47484
46947
  id: "query-no-rest-destructuring",
47485
46948
  title: "Rest destructuring on query result",
@@ -47491,7 +46954,7 @@ const queryNoRestDestructuring = defineRule({
47491
46954
  if (!isNodeOfType(node.id, "ObjectPattern")) return;
47492
46955
  if (!node.init) return;
47493
46956
  if (!node.id.properties?.some((property) => isNodeOfType(property, "RestElement"))) return;
47494
- const hookName = resolveTanstackQueryHookNameFromInitializer(node.init, context.scopes);
46957
+ const hookName = resolveHookNameFromInitializer(node.init, context.scopes);
47495
46958
  if (!hookName) return;
47496
46959
  context.report({
47497
46960
  node: node.id,
@@ -47735,7 +47198,7 @@ const reactCompilerNoManualMemoization = defineRule({
47735
47198
  });
47736
47199
  //#endregion
47737
47200
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
47738
- const MESSAGE$7 = "This JSX crashes because `React` isn't in scope.";
47201
+ const MESSAGE$6 = "This JSX crashes because `React` isn't in scope.";
47739
47202
  const reactInJsxScope = defineRule({
47740
47203
  id: "react-in-jsx-scope",
47741
47204
  title: "React not in scope for JSX",
@@ -47747,190 +47210,19 @@ const reactInJsxScope = defineRule({
47747
47210
  if (findVariableInitializer(node, "React")) return;
47748
47211
  context.report({
47749
47212
  node: node.name,
47750
- message: MESSAGE$7
47213
+ message: MESSAGE$6
47751
47214
  });
47752
47215
  },
47753
47216
  JSXFragment(node) {
47754
47217
  if (findVariableInitializer(node, "React")) return;
47755
47218
  context.report({
47756
47219
  node: node.openingFragment,
47757
- message: MESSAGE$7
47220
+ message: MESSAGE$6
47758
47221
  });
47759
47222
  }
47760
47223
  })
47761
47224
  });
47762
47225
  //#endregion
47763
- //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
47764
- const MESSAGE$6 = "React Markdown parses dynamic raw HTML when `rehype-raw` is enabled. Add `rehype-sanitize` to `rehypePlugins` or sanitize the markdown before rendering it.";
47765
- const REACT_MARKDOWN_MODULE = "react-markdown";
47766
- const REHYPE_RAW_MODULE = "rehype-raw";
47767
- const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
47768
- const DOMPURIFY_MODULES = new Set(["dompurify", "isomorphic-dompurify"]);
47769
- const REACT_MARKDOWN_NAMED_EXPORTS = new Set(["MarkdownAsync", "MarkdownHooks"]);
47770
- const REACT_MARKDOWN_NAMESPACE_EXPORTS = new Set(["default", ...REACT_MARKDOWN_NAMED_EXPORTS]);
47771
- const DEFAULT_EXPORT_NAMES = new Set(["default"]);
47772
- const getImportDeclaration = (symbol) => {
47773
- if (symbol.kind !== "import") return null;
47774
- const importDeclaration = symbol.declarationNode.parent;
47775
- return isNodeOfType(importDeclaration, "ImportDeclaration") ? importDeclaration : null;
47776
- };
47777
- const isImportFromModule = (symbol, moduleName) => getImportDeclaration(symbol)?.source.value === moduleName;
47778
- const isDefaultImportSymbol = (symbol, moduleName) => {
47779
- if (!isImportFromModule(symbol, moduleName)) return false;
47780
- return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "default";
47781
- };
47782
- const resolveImportedIdentifier = (node, scopes) => {
47783
- if (!isNodeOfType(node, "Identifier") && !isNodeOfType(node, "JSXIdentifier")) return null;
47784
- const symbol = resolveConstIdentifierAlias(node, scopes);
47785
- return symbol?.kind === "import" ? symbol : null;
47786
- };
47787
- const getStaticMemberParts = (node) => {
47788
- if (isNodeOfType(node, "MemberExpression")) {
47789
- if (node.computed || !isNodeOfType(node.property, "Identifier")) return null;
47790
- return {
47791
- object: node.object,
47792
- propertyName: node.property.name
47793
- };
47794
- }
47795
- if (isNodeOfType(node, "JSXMemberExpression")) {
47796
- if (!isNodeOfType(node.property, "JSXIdentifier")) return null;
47797
- return {
47798
- object: node.object,
47799
- propertyName: node.property.name
47800
- };
47801
- }
47802
- return null;
47803
- };
47804
- const isNamespaceMemberFromModule = (node, moduleName, memberNames, scopes) => {
47805
- const memberParts = getStaticMemberParts(node);
47806
- if (!memberParts || !memberNames.has(memberParts.propertyName)) return false;
47807
- const namespaceSymbol = resolveImportedIdentifier(memberParts.object, scopes);
47808
- return Boolean(namespaceSymbol && isImportFromModule(namespaceSymbol, moduleName) && isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier"));
47809
- };
47810
- const isReactMarkdownComponent = (node, scopes) => {
47811
- if (isNodeOfType(node, "JSXIdentifier")) {
47812
- const symbol = resolveImportedIdentifier(node, scopes);
47813
- if (!symbol || !isImportFromModule(symbol, REACT_MARKDOWN_MODULE)) return false;
47814
- if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return true;
47815
- const importedName = getImportedName(symbol.declarationNode);
47816
- return importedName === "default" || Boolean(importedName && REACT_MARKDOWN_NAMED_EXPORTS.has(importedName));
47817
- }
47818
- return isNamespaceMemberFromModule(node, REACT_MARKDOWN_MODULE, REACT_MARKDOWN_NAMESPACE_EXPORTS, scopes);
47819
- };
47820
- const isPluginFromModule = (rawNode, moduleName, scopes, visitedSymbolIds) => {
47821
- const node = stripParenExpression(rawNode);
47822
- if (isNodeOfType(node, "Identifier")) {
47823
- const symbol = resolveConstIdentifierAlias(node, scopes);
47824
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
47825
- if (isDefaultImportSymbol(symbol, moduleName)) return true;
47826
- if (symbol.kind !== "const" || !symbol.initializer) return false;
47827
- visitedSymbolIds.add(symbol.id);
47828
- return isPluginFromModule(symbol.initializer, moduleName, scopes, visitedSymbolIds);
47829
- }
47830
- if (isNamespaceMemberFromModule(node, moduleName, DEFAULT_EXPORT_NAMES, scopes)) return true;
47831
- if (!isNodeOfType(node, "ArrayExpression")) return false;
47832
- for (const element of node.elements) {
47833
- if (!element || isNodeOfType(element, "SpreadElement")) continue;
47834
- return isPluginFromModule(element, moduleName, scopes, visitedSymbolIds);
47835
- }
47836
- return false;
47837
- };
47838
- const collectPluginEntries = (rawNode, scopes, visitedSymbolIds) => {
47839
- const node = stripParenExpression(rawNode);
47840
- if (isNodeOfType(node, "Identifier")) {
47841
- const symbol = scopes.referenceFor(node)?.resolvedSymbol;
47842
- if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
47843
- visitedSymbolIds.add(symbol.id);
47844
- return collectPluginEntries(symbol.initializer, scopes, visitedSymbolIds);
47845
- }
47846
- if (!isNodeOfType(node, "ArrayExpression")) return null;
47847
- const entries = [];
47848
- for (const element of node.elements) {
47849
- if (!element) continue;
47850
- if (isNodeOfType(element, "SpreadElement")) {
47851
- const spreadEntries = collectPluginEntries(element.argument, scopes, new Set(visitedSymbolIds));
47852
- if (spreadEntries === null) return null;
47853
- entries.push(...spreadEntries);
47854
- continue;
47855
- }
47856
- entries.push(element);
47857
- }
47858
- return entries;
47859
- };
47860
- const findEffectiveExplicitAttribute = (attributes, attributeName) => {
47861
- for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
47862
- const attribute = attributes[attributeIndex];
47863
- if (!attribute || isNodeOfType(attribute, "JSXSpreadAttribute")) return null;
47864
- if (isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && attribute.name.name === attributeName) return attribute;
47865
- }
47866
- return null;
47867
- };
47868
- const getAttributeExpression = (attribute) => {
47869
- if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
47870
- return isNodeOfType(attribute.value.expression, "JSXEmptyExpression") ? null : attribute.value.expression;
47871
- };
47872
- const isDomPurifyNamespace = (node, scopes) => {
47873
- const symbol = resolveImportedIdentifier(node, scopes);
47874
- if (!symbol) return false;
47875
- const importDeclaration = getImportDeclaration(symbol);
47876
- if (!importDeclaration || !DOMPURIFY_MODULES.has(String(importDeclaration.source.value))) return false;
47877
- return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
47878
- };
47879
- const isDomPurifySanitizeCall = (node, scopes) => {
47880
- if (!isNodeOfType(node, "CallExpression")) return false;
47881
- const memberParts = getStaticMemberParts(stripParenExpression(node.callee));
47882
- return Boolean(memberParts && memberParts.propertyName === "sanitize" && isDomPurifyNamespace(memberParts.object, scopes));
47883
- };
47884
- const isStaticOrSanitizedMarkdownExpression = (rawNode, scopes, visitedSymbolIds) => {
47885
- const node = stripParenExpression(rawNode);
47886
- if (isNodeOfType(node, "Literal")) return true;
47887
- if (isNodeOfType(node, "TemplateLiteral")) return node.expressions.every((expression) => isStaticOrSanitizedMarkdownExpression(expression, scopes, new Set(visitedSymbolIds)));
47888
- if (isDomPurifySanitizeCall(node, scopes)) return true;
47889
- if (isNodeOfType(node, "ConditionalExpression")) return isStaticOrSanitizedMarkdownExpression(node.consequent, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.alternate, scopes, new Set(visitedSymbolIds));
47890
- if (isNodeOfType(node, "BinaryExpression") && node.operator === "+") return isStaticOrSanitizedMarkdownExpression(node.left, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.right, scopes, new Set(visitedSymbolIds));
47891
- if (!isNodeOfType(node, "Identifier")) return false;
47892
- const symbol = scopes.referenceFor(node)?.resolvedSymbol;
47893
- if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
47894
- visitedSymbolIds.add(symbol.id);
47895
- return isStaticOrSanitizedMarkdownExpression(symbol.initializer, scopes, visitedSymbolIds);
47896
- };
47897
- const hasDynamicUnsanitizedChildren = (openingElement, scopes) => {
47898
- const jsxElement = openingElement.parent;
47899
- if (!isNodeOfType(jsxElement, "JSXElement")) return false;
47900
- const meaningfulChildren = jsxElement.children.filter((child) => !isNodeOfType(child, "JSXText") || child.value.trim().length > 0);
47901
- if (meaningfulChildren.length > 0) return meaningfulChildren.some((child) => {
47902
- if (isNodeOfType(child, "JSXText")) return false;
47903
- if (!isNodeOfType(child, "JSXExpressionContainer")) return true;
47904
- if (isNodeOfType(child.expression, "JSXEmptyExpression")) return false;
47905
- return !isStaticOrSanitizedMarkdownExpression(child.expression, scopes, /* @__PURE__ */ new Set());
47906
- });
47907
- const childrenAttribute = findEffectiveExplicitAttribute(openingElement.attributes, "children");
47908
- if (!childrenAttribute) return false;
47909
- const childrenExpression = getAttributeExpression(childrenAttribute);
47910
- return Boolean(childrenExpression && !isStaticOrSanitizedMarkdownExpression(childrenExpression, scopes, /* @__PURE__ */ new Set()));
47911
- };
47912
- const reactMarkdownUnsanitizedRawHtml = defineRule({
47913
- id: "react-markdown-unsanitized-raw-html",
47914
- title: "Unsanitized raw HTML in React Markdown",
47915
- severity: "warn",
47916
- recommendation: "Add `rehype-sanitize` to `rehypePlugins` or sanitize dynamic markdown before rendering. `skipHtml` does not disable HTML already parsed by `rehype-raw`.",
47917
- create: skipNonProductionFiles((context) => ({ JSXOpeningElement(node) {
47918
- if (!isReactMarkdownComponent(node.name, context.scopes)) return;
47919
- const pluginsAttribute = findEffectiveExplicitAttribute(node.attributes, "rehypePlugins");
47920
- if (!pluginsAttribute) return;
47921
- const pluginsExpression = getAttributeExpression(pluginsAttribute);
47922
- if (!pluginsExpression) return;
47923
- const pluginEntries = collectPluginEntries(pluginsExpression, context.scopes, /* @__PURE__ */ new Set());
47924
- if (pluginEntries === null) return;
47925
- if (!pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_RAW_MODULE, context.scopes, /* @__PURE__ */ new Set()))) return;
47926
- if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
47927
- context.report({
47928
- node,
47929
- message: MESSAGE$6
47930
- });
47931
- } }))
47932
- });
47933
- //#endregion
47934
47226
  //#region src/plugin/utils/collect-react-redux-selector-aliases.ts
47935
47227
  const REACT_REDUX_MODULE = "react-redux";
47936
47228
  const collectReactReduxSelectorAliases = (programRoot) => {
@@ -56358,6 +55650,22 @@ const isInsideClassComponent = (node) => {
56358
55650
  }
56359
55651
  return false;
56360
55652
  };
55653
+ const hasShortCircuitAncestor = (descendant, ancestor) => {
55654
+ let current = descendant.parent;
55655
+ while (current && current !== ancestor) {
55656
+ if (isNodeOfType(current, "ConditionalExpression") && (isWithinRange(descendant, current.consequent) || isWithinRange(descendant, current.alternate))) return true;
55657
+ if (isNodeOfType(current, "LogicalExpression") && (current.operator === "&&" || current.operator === "||" || current.operator === "??") && isWithinRange(descendant, current.right)) return true;
55658
+ if (isNodeOfType(current, "AssignmentPattern") && isWithinRange(descendant, current.right)) return true;
55659
+ current = current.parent ?? null;
55660
+ }
55661
+ return false;
55662
+ };
55663
+ const isWithinRange = (descendant, sibling) => {
55664
+ const descendantSpan = descendant;
55665
+ const siblingSpan = sibling;
55666
+ if (typeof descendantSpan.start !== "number" || typeof siblingSpan.start !== "number" || typeof siblingSpan.end !== "number") return false;
55667
+ return descendantSpan.start >= siblingSpan.start && (descendantSpan.end ?? siblingSpan.end) <= siblingSpan.end;
55668
+ };
56361
55669
  const isInsideTry = (descendant, ancestor) => {
56362
55670
  let current = descendant.parent;
56363
55671
  while (current && current !== ancestor) {
@@ -56549,7 +55857,7 @@ const rulesOfHooks = defineRule({
56549
55857
  });
56550
55858
  return;
56551
55859
  }
56552
- if (isNodeConditionallyExecuted(node, enclosing.node)) {
55860
+ if (hasShortCircuitAncestor(node, enclosing.node)) {
56553
55861
  context.report({
56554
55862
  node: node.callee,
56555
55863
  message: buildConditionalMessage(hookName)
@@ -63164,17 +62472,6 @@ const reactDoctorRules = [
63164
62472
  requires: [...new Set(["react", ...reactInJsxScope.requires ?? []])]
63165
62473
  }
63166
62474
  },
63167
- {
63168
- key: "react-doctor/react-markdown-unsanitized-raw-html",
63169
- id: "react-markdown-unsanitized-raw-html",
63170
- source: "react-doctor",
63171
- originallyExternal: false,
63172
- rule: {
63173
- ...reactMarkdownUnsanitizedRawHtml,
63174
- framework: "global",
63175
- category: "Security"
63176
- }
63177
- },
63178
62475
  {
63179
62476
  key: "react-doctor/redux-useselector-inline-derivation",
63180
62477
  id: "redux-useselector-inline-derivation",