oxlint-plugin-react-doctor 0.7.7-dev.c79c897 → 0.7.7-dev.ce4179b

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 +46 -0
  2. package/dist/index.js +1731 -718
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,6 +7,13 @@ 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
10
17
  //#region src/plugin/utils/non-react-jsx-dialect.ts
11
18
  const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
12
19
  "solid-js",
@@ -21,17 +28,33 @@ const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
21
28
  "vidode"
22
29
  ]);
23
30
  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
+ ];
24
36
  const startsWithAny = (source, prefixes) => prefixes.some((prefix) => source === prefix || source.startsWith(`${prefix}/`));
25
- const fileImportsNonReactJsxDialect = (program) => {
37
+ const collectJsxRuntimeImports = (program) => {
38
+ let hasNonReactRuntime = false;
39
+ let hasReactRuntime = false;
26
40
  for (const statement of program.body) {
27
41
  if (!isNodeOfType(statement, "ImportDeclaration")) continue;
28
- const source = statement.source;
42
+ const importDeclaration = statement;
43
+ if (isTypeOnlyImport(importDeclaration)) continue;
44
+ const source = importDeclaration.source;
29
45
  const value = source && typeof source.value === "string" ? source.value : null;
30
46
  if (!value) continue;
31
- if (NON_REACT_JSX_DIALECT_PACKAGES.has(value)) return true;
32
- if (startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) return true;
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;
33
49
  }
34
- return false;
50
+ return {
51
+ hasNonReactRuntime,
52
+ hasReactRuntime
53
+ };
54
+ };
55
+ const fileImportsNonReactJsxDialect = (program) => {
56
+ const runtimeImports = collectJsxRuntimeImports(program);
57
+ return runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
35
58
  };
36
59
  const jsxAttributeIsNonReactDialectMarker = (openingNode) => {
37
60
  for (const attribute of openingNode.attributes) {
@@ -244,6 +267,7 @@ const VISITOR_NODE_NAME_PATTERN = /^[A-Z]/;
244
267
  const wrapCreateForReactJsxOnly = (create) => ((context) => {
245
268
  const innerVisitors = create(context);
246
269
  let fileIsNonReactJsx = false;
270
+ let fileImportsReactRuntime = false;
247
271
  const wrappedVisitors = {};
248
272
  for (const [key, visitor] of Object.entries(innerVisitors)) {
249
273
  if (typeof visitor !== "function") {
@@ -256,14 +280,16 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
256
280
  }
257
281
  if (key === "Program") {
258
282
  wrappedVisitors.Program = (node) => {
259
- fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
283
+ const runtimeImports = collectJsxRuntimeImports(node);
284
+ fileImportsReactRuntime = runtimeImports.hasReactRuntime;
285
+ fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
260
286
  visitor(node);
261
287
  };
262
288
  continue;
263
289
  }
264
290
  if (key === "JSXOpeningElement") {
265
291
  wrappedVisitors.JSXOpeningElement = (node) => {
266
- if (!fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
292
+ if (!fileImportsReactRuntime && !fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
267
293
  if (fileIsNonReactJsx) return;
268
294
  visitor(node);
269
295
  };
@@ -275,7 +301,9 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
275
301
  };
276
302
  }
277
303
  if (!("Program" in wrappedVisitors)) wrappedVisitors.Program = (node) => {
278
- fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
304
+ const runtimeImports = collectJsxRuntimeImports(node);
305
+ fileImportsReactRuntime = runtimeImports.hasReactRuntime;
306
+ fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
279
307
  };
280
308
  return wrappedVisitors;
281
309
  });
@@ -2312,7 +2340,7 @@ const anchorAmbiguousText = defineRule({
2312
2340
  });
2313
2341
  //#endregion
2314
2342
  //#region src/plugin/rules/a11y/anchor-has-content.ts
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`.";
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`.";
2316
2344
  const isTransComponentsTemplate = (node) => {
2317
2345
  let current = node.parent;
2318
2346
  while (current) {
@@ -2348,7 +2376,7 @@ const anchorHasContent = defineRule({
2348
2376
  if (isTransComponentsTemplate(node)) return;
2349
2377
  context.report({
2350
2378
  node: opening.name,
2351
- message: MESSAGE$61
2379
+ message: MESSAGE$62
2352
2380
  });
2353
2381
  } };
2354
2382
  }
@@ -2780,7 +2808,7 @@ const parseJsxValue = (value) => {
2780
2808
  };
2781
2809
  //#endregion
2782
2810
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
2783
- const MESSAGE$60 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2811
+ const MESSAGE$61 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2784
2812
  const mayBeContentEditable = (node) => {
2785
2813
  const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
2786
2814
  if (!attribute) return false;
@@ -2811,7 +2839,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2811
2839
  if (tabIndexValue === null || tabIndexValue >= -1) return;
2812
2840
  context.report({
2813
2841
  node: node.name,
2814
- message: MESSAGE$60
2842
+ message: MESSAGE$61
2815
2843
  });
2816
2844
  return;
2817
2845
  }
@@ -2819,7 +2847,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2819
2847
  if (mayBeContentEditable(node)) return;
2820
2848
  context.report({
2821
2849
  node: node.name,
2822
- message: MESSAGE$60
2850
+ message: MESSAGE$61
2823
2851
  });
2824
2852
  } })
2825
2853
  });
@@ -4314,6 +4342,397 @@ const findTransparentExpressionRoot = (node) => {
4314
4342
  return current;
4315
4343
  };
4316
4344
  //#endregion
4345
+ //#region src/plugin/utils/get-static-property-key-name.ts
4346
+ const getStaticPropertyKeyName = (node, options = {}) => {
4347
+ if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
4348
+ const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
4349
+ if (node.computed) {
4350
+ if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
4351
+ if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
4352
+ return null;
4353
+ }
4354
+ if (isNodeOfType(key, "Identifier")) return key.name;
4355
+ if (isNodeOfType(key, "Literal")) {
4356
+ if (typeof key.value === "string") return key.value;
4357
+ if (options.stringifyNonStringLiterals) return String(key.value);
4358
+ }
4359
+ return null;
4360
+ };
4361
+ //#endregion
4362
+ //#region src/plugin/utils/get-destructured-binding-property-name.ts
4363
+ const getDestructuredBindingPropertyName = (bindingIdentifier) => {
4364
+ let bindingNode = bindingIdentifier;
4365
+ if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
4366
+ const property = bindingNode.parent;
4367
+ if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
4368
+ return getStaticPropertyKeyName(property, { allowComputedString: true });
4369
+ };
4370
+ //#endregion
4371
+ //#region src/plugin/utils/get-static-property-name.ts
4372
+ const getStaticPropertyName = (memberExpression) => {
4373
+ const property = memberExpression.property;
4374
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
4375
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
4376
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
4377
+ return null;
4378
+ };
4379
+ //#endregion
4380
+ //#region src/plugin/utils/has-static-property-write-before.ts
4381
+ const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
4382
+ const potentiallyAliasedSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
4383
+ const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
4384
+ "CatchClause",
4385
+ "ConditionalExpression",
4386
+ "DoWhileStatement",
4387
+ "ForInStatement",
4388
+ "ForOfStatement",
4389
+ "ForStatement",
4390
+ "IfStatement",
4391
+ "LogicalExpression",
4392
+ "SwitchCase",
4393
+ "SwitchStatement",
4394
+ "TryStatement",
4395
+ "WhileStatement"
4396
+ ]);
4397
+ const getResolvedStaticPropertyName = (memberExpression, scopes) => {
4398
+ if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
4399
+ const directPropertyName = getStaticPropertyName(memberExpression);
4400
+ if (directPropertyName || !memberExpression.computed) return directPropertyName;
4401
+ const property = stripParenExpression(memberExpression.property);
4402
+ if (!isNodeOfType(property, "Identifier")) return null;
4403
+ const propertySymbol = resolveConstIdentifierAlias(property, scopes);
4404
+ const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
4405
+ return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
4406
+ };
4407
+ const collectScopeSymbols = (scope, symbols) => {
4408
+ symbols.push(...scope.symbols);
4409
+ for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
4410
+ };
4411
+ const getEquivalentSymbols = (identifier, scopes) => {
4412
+ const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
4413
+ if (!rootSymbol) return [];
4414
+ let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
4415
+ if (!symbolsByRootId) {
4416
+ symbolsByRootId = /* @__PURE__ */ new Map();
4417
+ equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
4418
+ }
4419
+ const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
4420
+ if (cachedSymbols) return cachedSymbols;
4421
+ const allSymbols = [];
4422
+ collectScopeSymbols(scopes.rootScope, allSymbols);
4423
+ const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
4424
+ symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
4425
+ return equivalentSymbols;
4426
+ };
4427
+ const getDirectAliasSourceSymbol = (expression, scopes) => {
4428
+ const unwrappedExpression = stripParenExpression(expression);
4429
+ return isNodeOfType(unwrappedExpression, "Identifier") ? scopes.symbolFor(unwrappedExpression) : null;
4430
+ };
4431
+ const getDirectAssignmentSourceSymbol = (identifier, scopes) => {
4432
+ const assignmentTarget = findTransparentExpressionRoot(identifier);
4433
+ const parent = assignmentTarget.parent;
4434
+ if (!parent || !isNodeOfType(parent, "AssignmentExpression") || parent.operator !== "=" || parent.left !== assignmentTarget) return null;
4435
+ return getDirectAliasSourceSymbol(parent.right, scopes);
4436
+ };
4437
+ const isDirectAliasSourceReference = (identifier) => {
4438
+ const aliasSource = findTransparentExpressionRoot(identifier);
4439
+ const parent = aliasSource.parent;
4440
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === aliasSource && (isNodeOfType(parent.id, "Identifier") || isNodeOfType(parent.id, "ObjectPattern"))) return true;
4441
+ return Boolean(parent && isNodeOfType(parent, "AssignmentExpression") && parent.operator === "=" && parent.right === aliasSource && isNodeOfType(stripParenExpression(parent.left), "Identifier"));
4442
+ };
4443
+ const isDirectAliasOfKnownSymbol = (symbol, knownSymbolIds, scopes) => {
4444
+ if (symbol.initializer && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && symbol.declarationNode.id === symbol.bindingIdentifier) {
4445
+ const initializerSymbol = getDirectAliasSourceSymbol(symbol.initializer, scopes);
4446
+ if (initializerSymbol && knownSymbolIds.has(initializerSymbol.id)) return true;
4447
+ }
4448
+ return symbol.references.some((reference) => {
4449
+ const assignmentSourceSymbol = getDirectAssignmentSourceSymbol(reference.identifier, scopes);
4450
+ return Boolean(assignmentSourceSymbol && knownSymbolIds.has(assignmentSourceSymbol.id));
4451
+ });
4452
+ };
4453
+ const getPotentiallyAliasedSymbols = (identifier, scopes) => {
4454
+ const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
4455
+ if (!rootSymbol) return [];
4456
+ let symbolsByRootId = potentiallyAliasedSymbolsByAnalysis.get(scopes);
4457
+ if (!symbolsByRootId) {
4458
+ symbolsByRootId = /* @__PURE__ */ new Map();
4459
+ potentiallyAliasedSymbolsByAnalysis.set(scopes, symbolsByRootId);
4460
+ }
4461
+ const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
4462
+ if (cachedSymbols) return cachedSymbols;
4463
+ const allSymbols = [];
4464
+ collectScopeSymbols(scopes.rootScope, allSymbols);
4465
+ const aliasedSymbolIds = new Set([rootSymbol.id]);
4466
+ let didAddAlias = true;
4467
+ while (didAddAlias) {
4468
+ didAddAlias = false;
4469
+ for (const symbol of allSymbols) {
4470
+ if (aliasedSymbolIds.has(symbol.id)) continue;
4471
+ if (!isDirectAliasOfKnownSymbol(symbol, aliasedSymbolIds, scopes)) continue;
4472
+ aliasedSymbolIds.add(symbol.id);
4473
+ didAddAlias = true;
4474
+ }
4475
+ }
4476
+ const aliasedSymbols = allSymbols.filter((symbol) => aliasedSymbolIds.has(symbol.id));
4477
+ symbolsByRootId.set(rootSymbol.id, aliasedSymbols);
4478
+ return aliasedSymbols;
4479
+ };
4480
+ const findExecutionBoundary = (node) => {
4481
+ let current = node;
4482
+ while (current) {
4483
+ if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
4484
+ current = current.parent ?? null;
4485
+ }
4486
+ return null;
4487
+ };
4488
+ const isOnUnconditionalPath = (node, boundary) => {
4489
+ let current = node.parent ?? null;
4490
+ while (current && current !== boundary) {
4491
+ if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
4492
+ current = current.parent ?? null;
4493
+ }
4494
+ return current === boundary;
4495
+ };
4496
+ const findFunctionBindingIdentifier = (functionNode) => {
4497
+ if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
4498
+ const parent = functionNode.parent;
4499
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
4500
+ return null;
4501
+ };
4502
+ const findDirectCall = (identifier) => {
4503
+ let callee = identifier;
4504
+ let parent = callee.parent;
4505
+ while (parent && stripParenExpression(parent) === identifier) {
4506
+ callee = parent;
4507
+ parent = callee.parent;
4508
+ }
4509
+ return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
4510
+ };
4511
+ const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
4512
+ if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
4513
+ visitedFunctionNodes.add(functionNode);
4514
+ const referenceBoundary = findExecutionBoundary(referenceNode);
4515
+ if (!referenceBoundary) return false;
4516
+ const invocationCalls = [];
4517
+ const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
4518
+ if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
4519
+ const call = findDirectCall(reference.identifier);
4520
+ if (call) invocationCalls.push(call);
4521
+ }
4522
+ else {
4523
+ const call = findDirectCall(functionNode);
4524
+ if (call) invocationCalls.push(call);
4525
+ }
4526
+ return invocationCalls.some((call) => {
4527
+ if (call.range[0] >= referenceNode.range[0]) return false;
4528
+ const callBoundary = findExecutionBoundary(call);
4529
+ if (!callBoundary) return false;
4530
+ if (callBoundary === referenceBoundary) return true;
4531
+ if (!isFunctionLike$2(callBoundary)) return false;
4532
+ return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
4533
+ });
4534
+ };
4535
+ const isMemberWriteTarget = (memberExpression) => {
4536
+ const parent = memberExpression.parent;
4537
+ if (!parent) return false;
4538
+ if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
4539
+ if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
4540
+ return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
4541
+ };
4542
+ const getMemberWriteTarget = (identifier) => {
4543
+ let parent = identifier.parent;
4544
+ while (parent && stripParenExpression(parent) === identifier) parent = parent.parent;
4545
+ if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== identifier || !isMemberWriteTarget(parent)) return null;
4546
+ return parent;
4547
+ };
4548
+ const getStaticPropertyWriteTarget = (identifier, propertyName, scopes) => {
4549
+ const writeTarget = getMemberWriteTarget(identifier);
4550
+ return writeTarget && getResolvedStaticPropertyName(writeTarget, scopes) === propertyName ? writeTarget : null;
4551
+ };
4552
+ const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
4553
+ const writeTarget = getStaticPropertyWriteTarget(reference.identifier, propertyName, scopes);
4554
+ if (!writeTarget) return false;
4555
+ const writeBoundary = findExecutionBoundary(writeTarget);
4556
+ const referenceBoundary = findExecutionBoundary(referenceNode);
4557
+ if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(writeTarget, writeBoundary)) return false;
4558
+ if (writeBoundary === referenceBoundary) return writeTarget.range[0] < referenceNode.range[0];
4559
+ return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
4560
+ });
4561
+ const isStableStaticPropertyReference = (identifier) => {
4562
+ if (isDirectAliasSourceReference(identifier)) return true;
4563
+ const identifierRoot = findTransparentExpressionRoot(identifier);
4564
+ const memberExpression = identifierRoot.parent;
4565
+ return Boolean(memberExpression && isNodeOfType(memberExpression, "MemberExpression") && stripParenExpression(memberExpression.object) === identifierRoot);
4566
+ };
4567
+ const hasPossibleStaticPropertyWrite = (identifier, propertyName, scopes) => {
4568
+ if (!isNodeOfType(identifier, "Identifier")) return false;
4569
+ return getPotentiallyAliasedSymbols(identifier, scopes).some((symbol) => symbol.references.some((reference) => {
4570
+ const writeTarget = getMemberWriteTarget(reference.identifier);
4571
+ if (!writeTarget) return false;
4572
+ const writtenPropertyName = getResolvedStaticPropertyName(writeTarget, scopes);
4573
+ return writtenPropertyName === null || writtenPropertyName === propertyName;
4574
+ }));
4575
+ };
4576
+ const hasPossibleStaticPropertyMutationOrEscape = (identifier, propertyName, scopes) => {
4577
+ if (!isNodeOfType(identifier, "Identifier")) return false;
4578
+ if (hasPossibleStaticPropertyWrite(identifier, propertyName, scopes)) return true;
4579
+ return getPotentiallyAliasedSymbols(identifier, scopes).some((symbol) => symbol.references.some((reference) => !isStableStaticPropertyReference(reference.identifier)));
4580
+ };
4581
+ const hasPossibleStaticMemberCallWrite = (callExpression, scopes) => {
4582
+ const unwrappedCallExpression = stripParenExpression(callExpression);
4583
+ if (!isNodeOfType(unwrappedCallExpression, "CallExpression")) return false;
4584
+ const callee = stripParenExpression(unwrappedCallExpression.callee);
4585
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
4586
+ const propertyName = getStaticPropertyName(callee);
4587
+ if (propertyName === null) return false;
4588
+ const receiver = stripParenExpression(callee.object);
4589
+ return isNodeOfType(receiver, "Identifier") && hasPossibleStaticPropertyWrite(receiver, propertyName, scopes);
4590
+ };
4591
+ const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
4592
+ if (!isNodeOfType(identifier, "Identifier")) return false;
4593
+ return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
4594
+ };
4595
+ //#endregion
4596
+ //#region src/plugin/utils/find-enclosing-function.ts
4597
+ const findEnclosingFunction$1 = (node) => {
4598
+ let cursor = node.parent;
4599
+ while (cursor) {
4600
+ if (isFunctionLike$2(cursor)) return cursor;
4601
+ cursor = cursor.parent ?? null;
4602
+ }
4603
+ return null;
4604
+ };
4605
+ //#endregion
4606
+ //#region src/plugin/utils/has-symbol-write-before.ts
4607
+ const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
4608
+ if (reference.flag === "read") return false;
4609
+ const writeFunction = findEnclosingFunction$1(reference.identifier);
4610
+ if (writeFunction === findEnclosingFunction$1(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
4611
+ if (!writeFunction) return true;
4612
+ return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
4613
+ });
4614
+ //#endregion
4615
+ //#region src/plugin/utils/get-order-independent-local-function.ts
4616
+ const COMMUTATIVE_COMPOUND_ASSIGNMENT_OPERATORS = new Set([
4617
+ "+=",
4618
+ "-=",
4619
+ "*=",
4620
+ "/=",
4621
+ "%=",
4622
+ "&=",
4623
+ "^=",
4624
+ "|="
4625
+ ]);
4626
+ const isPureParameterExpression = (expression, parameterNames) => {
4627
+ const unwrappedExpression = stripParenExpression(expression);
4628
+ if (isNodeOfType(unwrappedExpression, "Literal")) return true;
4629
+ if (isNodeOfType(unwrappedExpression, "Identifier")) return parameterNames.has(unwrappedExpression.name);
4630
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression") || isNodeOfType(unwrappedExpression, "LogicalExpression")) return isPureParameterExpression(unwrappedExpression.left, parameterNames) && isPureParameterExpression(unwrappedExpression.right, parameterNames);
4631
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression")) return unwrappedExpression.operator !== "delete" && isPureParameterExpression(unwrappedExpression.argument, parameterNames);
4632
+ if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isPureParameterExpression(unwrappedExpression.test, parameterNames) && isPureParameterExpression(unwrappedExpression.consequent, parameterNames) && isPureParameterExpression(unwrappedExpression.alternate, parameterNames);
4633
+ if (isNodeOfType(unwrappedExpression, "TemplateLiteral")) return unwrappedExpression.expressions.every((nestedExpression) => isPureParameterExpression(nestedExpression, parameterNames));
4634
+ return false;
4635
+ };
4636
+ const isOrderIndependentPromiseResolveCall = (expression, parameterNames, scopes) => {
4637
+ const unwrappedExpression = stripParenExpression(expression);
4638
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
4639
+ if (!unwrappedExpression.arguments.every((argument) => !isNodeOfType(argument, "SpreadElement") && isPureParameterExpression(argument, parameterNames))) return false;
4640
+ const callee = stripParenExpression(unwrappedExpression.callee);
4641
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
4642
+ if (getStaticPropertyName(callee) !== "resolve") return false;
4643
+ const receiver = stripParenExpression(callee.object);
4644
+ return isNodeOfType(receiver, "Identifier") && receiver.name === "Promise" && scopes.isGlobalReference(receiver);
4645
+ };
4646
+ const isHarmlessPromiseResolveAwait = (statement, parameterNames, scopes) => {
4647
+ if (!isNodeOfType(statement, "ExpressionStatement") || !isNodeOfType(statement.expression, "AwaitExpression")) return false;
4648
+ return isOrderIndependentPromiseResolveCall(statement.expression.argument, parameterNames, scopes);
4649
+ };
4650
+ const isCommutativeParameterMutation = (statement, parameterNames) => {
4651
+ if (!isNodeOfType(statement, "ExpressionStatement") || !isNodeOfType(statement.expression, "AssignmentExpression") || !COMMUTATIVE_COMPOUND_ASSIGNMENT_OPERATORS.has(statement.expression.operator)) return false;
4652
+ const mutationTarget = stripParenExpression(statement.expression.left);
4653
+ if (!isNodeOfType(mutationTarget, "MemberExpression")) return false;
4654
+ if (getStaticPropertyName(mutationTarget) === null) return false;
4655
+ const receiver = stripParenExpression(mutationTarget.object);
4656
+ return isNodeOfType(receiver, "Identifier") && parameterNames.has(receiver.name) && isNodeOfType(stripParenExpression(statement.expression.right), "Literal");
4657
+ };
4658
+ const isOrderIndependentFunction = (functionNode, scopes) => {
4659
+ if (!isFunctionLike$2(functionNode)) return false;
4660
+ const parameterNames = /* @__PURE__ */ new Set();
4661
+ for (const parameter of functionNode.params) {
4662
+ if (!isNodeOfType(parameter, "Identifier")) return false;
4663
+ parameterNames.add(parameter.name);
4664
+ }
4665
+ if (!functionNode.async) {
4666
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isOrderIndependentPromiseResolveCall(functionNode.body, parameterNames, scopes);
4667
+ if (functionNode.body.body.length !== 1) return false;
4668
+ const [returnStatement] = functionNode.body.body;
4669
+ return Boolean(isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument && isOrderIndependentPromiseResolveCall(returnStatement.argument, parameterNames, scopes));
4670
+ }
4671
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isPureParameterExpression(functionNode.body, parameterNames);
4672
+ const statements = functionNode.body.body;
4673
+ for (let statementIndex = 0; statementIndex < statements.length; statementIndex++) {
4674
+ const statement = statements[statementIndex];
4675
+ const isTerminalStatement = statementIndex === statements.length - 1;
4676
+ if (isHarmlessPromiseResolveAwait(statement, parameterNames, scopes)) continue;
4677
+ if (isNodeOfType(statement, "ExpressionStatement") && isPureParameterExpression(statement.expression, parameterNames)) continue;
4678
+ if (isCommutativeParameterMutation(statement, parameterNames)) return isTerminalStatement;
4679
+ if (!isNodeOfType(statement, "ReturnStatement") || !isTerminalStatement) return false;
4680
+ return !statement.argument || isPureParameterExpression(statement.argument, parameterNames) || isOrderIndependentPromiseResolveCall(statement.argument, parameterNames, scopes);
4681
+ }
4682
+ return true;
4683
+ };
4684
+ const getObjectPropertyName = (property) => {
4685
+ if (!isNodeOfType(property, "Property")) return null;
4686
+ return getStaticPropertyKeyName(property, { allowComputedString: true });
4687
+ };
4688
+ const resolveOrderIndependentObjectPropertyFunction = (objectExpression, propertyName, callExpression, scopes, visitedSymbolIds) => {
4689
+ const unwrappedObject = stripParenExpression(objectExpression);
4690
+ if (isNodeOfType(unwrappedObject, "Identifier")) {
4691
+ const symbol = scopes.symbolFor(unwrappedObject);
4692
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, callExpression, scopes) || hasPossibleStaticPropertyMutationOrEscape(unwrappedObject, propertyName, scopes)) return null;
4693
+ visitedSymbolIds.add(symbol.id);
4694
+ return resolveOrderIndependentObjectPropertyFunction(symbol.initializer, propertyName, callExpression, scopes, visitedSymbolIds);
4695
+ }
4696
+ if (!isNodeOfType(unwrappedObject, "ObjectExpression")) return null;
4697
+ let matchingProperty = null;
4698
+ for (const property of unwrappedObject.properties) {
4699
+ if (!isNodeOfType(property, "Property")) return null;
4700
+ if (property.kind !== "init") return null;
4701
+ const candidatePropertyName = getObjectPropertyName(property);
4702
+ if (candidatePropertyName === null) return null;
4703
+ if (candidatePropertyName === propertyName) matchingProperty = property;
4704
+ }
4705
+ if (!matchingProperty || !isNodeOfType(matchingProperty, "Property")) return null;
4706
+ const propertyValue = stripParenExpression(matchingProperty.value);
4707
+ if (isFunctionLike$2(propertyValue)) return isOrderIndependentFunction(propertyValue, scopes) ? propertyValue : null;
4708
+ return resolveOrderIndependentLocalFunction(propertyValue, callExpression, scopes, visitedSymbolIds);
4709
+ };
4710
+ const resolveOrderIndependentLocalFunction = (callee, callExpression, scopes, visitedSymbolIds) => {
4711
+ const unwrappedCallee = stripParenExpression(callee);
4712
+ if (isNodeOfType(unwrappedCallee, "MemberExpression")) {
4713
+ const propertyName = getStaticPropertyName(unwrappedCallee);
4714
+ if (propertyName === null) return null;
4715
+ return resolveOrderIndependentObjectPropertyFunction(stripParenExpression(unwrappedCallee.object), propertyName, callExpression, scopes, visitedSymbolIds);
4716
+ }
4717
+ if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
4718
+ const symbol = scopes.symbolFor(unwrappedCallee);
4719
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, callExpression, scopes)) return null;
4720
+ visitedSymbolIds.add(symbol.id);
4721
+ if (!symbol.initializer) return null;
4722
+ const initializer = stripParenExpression(symbol.initializer);
4723
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
4724
+ if (destructuredPropertyName !== null) return resolveOrderIndependentObjectPropertyFunction(initializer, destructuredPropertyName, callExpression, scopes, visitedSymbolIds);
4725
+ if (isFunctionLike$2(initializer)) return isOrderIndependentFunction(initializer, scopes) ? initializer : null;
4726
+ if (symbol.kind !== "const") return null;
4727
+ return resolveOrderIndependentLocalFunction(initializer, callExpression, scopes, visitedSymbolIds);
4728
+ };
4729
+ const getOrderIndependentLocalFunction = (callExpression, scopes) => {
4730
+ const unwrappedCall = stripParenExpression(callExpression);
4731
+ if (!isNodeOfType(unwrappedCall, "CallExpression")) return null;
4732
+ if (hasPossibleStaticMemberCallWrite(unwrappedCall, scopes)) return null;
4733
+ return resolveOrderIndependentLocalFunction(unwrappedCall.callee, unwrappedCall, scopes, /* @__PURE__ */ new Set());
4734
+ };
4735
+ //#endregion
4317
4736
  //#region src/plugin/utils/is-inline-function-expression.ts
4318
4737
  /**
4319
4738
  * Type-guard for the two "inline function expression" ESTree forms:
@@ -4347,13 +4766,15 @@ const isIntentionalSequencingCallee = (callee) => {
4347
4766
  if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier")) return INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(callee.property.name);
4348
4767
  return false;
4349
4768
  };
4350
- const isAwaitingSleepLikeCall = (awaitNode) => {
4769
+ const isAwaitingSleepLikeCall = (awaitNode, context) => {
4351
4770
  if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
4352
4771
  const argument = awaitNode.argument;
4353
4772
  if (!argument) return false;
4354
4773
  if (!isNodeOfType(argument, "CallExpression")) return false;
4774
+ if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) return false;
4355
4775
  return isIntentionalSequencingCallee(argument.callee);
4356
4776
  };
4777
+ const isAwaitingPossiblyMutatedMemberCall = (awaitNode, context) => isNodeOfType(awaitNode, "AwaitExpression") && Boolean(awaitNode.argument) && hasPossibleStaticMemberCallWrite(awaitNode.argument, context.scopes);
4357
4778
  const PROMISE_CONCURRENCY_METHODS = new Set([
4358
4779
  "all",
4359
4780
  "allSettled",
@@ -4396,7 +4817,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
4396
4817
  });
4397
4818
  return isWaitLike;
4398
4819
  };
4399
- const isIntentionallySequentialAwait = (awaitNode) => isAwaitingSleepLikeCall(awaitNode) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
4820
+ const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
4400
4821
  const collectPatternIdentifiers = (pattern, target) => {
4401
4822
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
4402
4823
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -4587,12 +5008,12 @@ const getLoopLabelName = (loopNode) => {
4587
5008
  if (isNodeOfType(parent, "LabeledStatement") && isNodeOfType(parent.label, "Identifier")) return parent.label.name;
4588
5009
  return null;
4589
5010
  };
4590
- const loopBodyHasIntentionallySequentialAwait = (block) => {
5011
+ const loopBodyHasIntentionallySequentialAwait = (block, context) => {
4591
5012
  let foundIntentional = false;
4592
5013
  walkAst(block, (child) => {
4593
5014
  if (foundIntentional) return false;
4594
5015
  if (isInlineFunctionExpression(child) || isNodeOfType(child, "FunctionDeclaration")) return false;
4595
- if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child)) {
5016
+ if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child, context)) {
4596
5017
  foundIntentional = true;
4597
5018
  return false;
4598
5019
  }
@@ -4697,7 +5118,7 @@ const asyncAwaitInLoop = defineRule({
4697
5118
  const inspectLoop = (loopNode, label) => {
4698
5119
  const loopBody = loopNode.body;
4699
5120
  if (!loopBody) return;
4700
- if (loopBodyHasIntentionallySequentialAwait(loopBody)) return;
5121
+ if (loopBodyHasIntentionallySequentialAwait(loopBody, context)) return;
4701
5122
  if ((isNodeOfType(loopNode, "WhileStatement") || isNodeOfType(loopNode, "DoWhileStatement")) && isLoopTestDependentOnBodyState(loopNode.test, loopBody)) return;
4702
5123
  if (hasLoopCarriedDependency(loopBody)) return;
4703
5124
  if (loopBodyHasAwaitDependentEarlyExit(loopBody, getLoopLabelName(loopNode))) return;
@@ -4957,7 +5378,7 @@ const guardConsequentPerformsSideEffects = (consequent) => {
4957
5378
  });
4958
5379
  return performsSideEffects;
4959
5380
  };
4960
- const findEnclosingFunction$1 = (node) => {
5381
+ const findEnclosingFunction = (node) => {
4961
5382
  let ancestor = node.parent;
4962
5383
  while (ancestor) {
4963
5384
  if (isFunctionLike$2(ancestor)) return ancestor;
@@ -4970,7 +5391,7 @@ const guardTestReadsReassignedLocal = (test, guardStatement) => {
4970
5391
  const testIdentifierNames = /* @__PURE__ */ new Set();
4971
5392
  collectReferenceIdentifierNames(test, testIdentifierNames);
4972
5393
  if (testIdentifierNames.size === 0) return false;
4973
- const enclosingFunction = findEnclosingFunction$1(guardStatement);
5394
+ const enclosingFunction = findEnclosingFunction(guardStatement);
4974
5395
  if (!enclosingFunction || !isFunctionLike$2(enclosingFunction) || !enclosingFunction.body) return false;
4975
5396
  let readsReassignedLocal = false;
4976
5397
  walkAst(enclosingFunction.body, (child) => {
@@ -5141,19 +5562,36 @@ const isIntentionalSequencingAwait = (awaitedCall) => {
5141
5562
  return getCalleeIdentifierTrail(awaitedCall).some((name) => INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(name));
5142
5563
  };
5143
5564
  const isBareExpressionAwait = (statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "AwaitExpression");
5565
+ const hasOrderIndependentBareAwaitArguments = (callExpression) => {
5566
+ const unwrappedCallExpression = stripParenExpression(callExpression);
5567
+ if (!isNodeOfType(unwrappedCallExpression, "CallExpression")) return false;
5568
+ return unwrappedCallExpression.arguments.every((argument) => {
5569
+ if (isNodeOfType(argument, "SpreadElement")) return false;
5570
+ const unwrappedArgument = stripParenExpression(argument);
5571
+ return isNodeOfType(unwrappedArgument, "Identifier") || isNodeOfType(unwrappedArgument, "Literal");
5572
+ });
5573
+ };
5144
5574
  const isNonCallAwait = (statement) => {
5145
5575
  const awaitedExpression = getAwaitedCall(statement);
5146
5576
  if (!awaitedExpression) return false;
5147
5577
  const stripped = stripParenExpression(awaitedExpression);
5148
5578
  return !isNodeOfType(stripped, "CallExpression") && !isNodeOfType(stripped, "NewExpression") && !isNodeOfType(stripped, "ImportExpression");
5149
5579
  };
5150
- const sequenceContainsSerializationSignal = (statements) => {
5580
+ const sequenceContainsSerializationSignal = (statements, context) => {
5581
+ let bareAwaitFunction = null;
5151
5582
  for (const statement of statements) {
5152
- if (isBareExpressionAwait(statement)) return true;
5153
5583
  if (isNonCallAwait(statement)) return true;
5154
5584
  const awaitedCall = getAwaitedCall(statement);
5585
+ if (awaitedCall && hasPossibleStaticMemberCallWrite(awaitedCall, context.scopes)) return true;
5586
+ const orderIndependentFunction = awaitedCall ? getOrderIndependentLocalFunction(awaitedCall, context.scopes) : null;
5587
+ if (isBareExpressionAwait(statement)) {
5588
+ if (orderIndependentFunction === null) return true;
5589
+ if (!awaitedCall || !hasOrderIndependentBareAwaitArguments(awaitedCall)) return true;
5590
+ if (bareAwaitFunction !== null && bareAwaitFunction !== orderIndependentFunction) return true;
5591
+ bareAwaitFunction = orderIndependentFunction;
5592
+ }
5155
5593
  if (isOrderedUiFlowAwait(awaitedCall)) return true;
5156
- if (isIntentionalSequencingAwait(awaitedCall)) return true;
5594
+ if (isIntentionalSequencingAwait(awaitedCall) && orderIndependentFunction === null) return true;
5157
5595
  }
5158
5596
  return false;
5159
5597
  };
@@ -5211,7 +5649,7 @@ const asyncParallel = defineRule({
5211
5649
  const consecutiveAwaitStatements = [];
5212
5650
  const flushConsecutiveAwaits = () => {
5213
5651
  if (consecutiveAwaitStatements.length >= 3) {
5214
- if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements)) reportIfIndependent(consecutiveAwaitStatements, context);
5652
+ if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements, context)) reportIfIndependent(consecutiveAwaitStatements, context);
5215
5653
  }
5216
5654
  consecutiveAwaitStatements.length = 0;
5217
5655
  };
@@ -5224,7 +5662,7 @@ const asyncParallel = defineRule({
5224
5662
  });
5225
5663
  //#endregion
5226
5664
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
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.";
5665
+ 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.";
5228
5666
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
5229
5667
  const STORAGE_GLOBALS = new Set([
5230
5668
  "window",
@@ -5289,7 +5727,7 @@ const authTokenInWebStorage = defineRule({
5289
5727
  if (keyString === null || !isAuthCredentialKey(keyString)) return;
5290
5728
  context.report({
5291
5729
  node,
5292
- message: MESSAGE$59
5730
+ message: MESSAGE$60
5293
5731
  });
5294
5732
  },
5295
5733
  AssignmentExpression(node) {
@@ -5300,7 +5738,7 @@ const authTokenInWebStorage = defineRule({
5300
5738
  if (!propertyName || !isAuthCredentialKey(propertyName)) return;
5301
5739
  context.report({
5302
5740
  node: target,
5303
- message: MESSAGE$59
5741
+ message: MESSAGE$60
5304
5742
  });
5305
5743
  }
5306
5744
  }))
@@ -5439,7 +5877,7 @@ const CI_INSTALL_NEAR_SECRET_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b
5439
5877
  const INSTALL_COMMAND_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b/i;
5440
5878
  const SECRET_REFERENCE_PATTERN = /\bsecrets\.[A-Z0-9_]+/;
5441
5879
  const IGNORE_SCRIPTS_FLAG_PATTERN = /--ignore-scripts\b/;
5442
- const MESSAGE$58 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5880
+ const MESSAGE$59 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5443
5881
  const isWorkflowPath = (relativePath) => /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath);
5444
5882
  const scanWorkflowContent = (content) => {
5445
5883
  const lines = content.split("\n");
@@ -5484,7 +5922,7 @@ const scanWorkflowContent = (content) => {
5484
5922
  const installLineOffset = Math.max(step.lines.findIndex((stepLine) => INSTALL_COMMAND_PATTERN.test(stepLine)), 0);
5485
5923
  const installColumnIndex = step.lines[installLineOffset].search(INSTALL_COMMAND_PATTERN);
5486
5924
  return [{
5487
- message: MESSAGE$58,
5925
+ message: MESSAGE$59,
5488
5926
  line: step.startLineIndex + installLineOffset + 1,
5489
5927
  column: (installColumnIndex === -1 ? 0 : installColumnIndex) + 1
5490
5928
  }];
@@ -5494,7 +5932,7 @@ const scanWorkflowContent = (content) => {
5494
5932
  const scanNonWorkflowConfig = scanByPattern({
5495
5933
  shouldScan: (file) => isConfigOrCiPath(file.relativePath) && !file.relativePath.endsWith("package.json") && !isWorkflowPath(file.relativePath),
5496
5934
  pattern: CI_INSTALL_NEAR_SECRET_PATTERN,
5497
- message: MESSAGE$58
5935
+ message: MESSAGE$59
5498
5936
  });
5499
5937
  const scan = (file) => {
5500
5938
  if (isWorkflowPath(file.relativePath)) return scanWorkflowContent(file.content);
@@ -5989,21 +6427,6 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5989
6427
  }
5990
6428
  });
5991
6429
  //#endregion
5992
- //#region src/plugin/utils/get-static-property-key-name.ts
5993
- const getStaticPropertyKeyName = (node, options = {}) => {
5994
- if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition")) return null;
5995
- if (node.computed) {
5996
- if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
5997
- return null;
5998
- }
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);
6003
- }
6004
- return null;
6005
- };
6006
- //#endregion
6007
6430
  //#region src/plugin/utils/are-expressions-structurally-equal.ts
6008
6431
  const areExpressionsStructurallyEqual = (a, b) => {
6009
6432
  if (!a || !b) return a === b;
@@ -6261,7 +6684,7 @@ const isPureEventBlockerHandler = (attribute) => {
6261
6684
  };
6262
6685
  //#endregion
6263
6686
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
6264
- const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6687
+ const MESSAGE$58 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6265
6688
  const KEY_HANDLERS = [
6266
6689
  "onKeyUp",
6267
6690
  "onKeyDown",
@@ -6472,7 +6895,7 @@ const clickEventsHaveKeyEvents = defineRule({
6472
6895
  if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6473
6896
  context.report({
6474
6897
  node: node.name,
6475
- message: MESSAGE$57
6898
+ message: MESSAGE$58
6476
6899
  });
6477
6900
  } };
6478
6901
  }
@@ -6687,15 +7110,6 @@ const getDirectConstInitializer = (symbol) => {
6687
7110
  return symbol.initializer;
6688
7111
  };
6689
7112
  //#endregion
6690
- //#region src/plugin/utils/get-static-property-name.ts
6691
- const getStaticPropertyName = (memberExpression) => {
6692
- const property = memberExpression.property;
6693
- if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
6694
- if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
6695
- if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
6696
- return null;
6697
- };
6698
- //#endregion
6699
7113
  //#region src/plugin/utils/get-range-start.ts
6700
7114
  const getRangeStart = (node) => {
6701
7115
  const rangeStart = node.range?.[0];
@@ -7091,16 +7505,6 @@ const collectFunctionReturnStatements = (functionNode) => {
7091
7505
  return returnStatements;
7092
7506
  };
7093
7507
  //#endregion
7094
- //#region src/plugin/utils/find-enclosing-function.ts
7095
- const findEnclosingFunction = (node) => {
7096
- let cursor = node.parent;
7097
- while (cursor) {
7098
- if (isFunctionLike$2(cursor)) return cursor;
7099
- cursor = cursor.parent ?? null;
7100
- }
7101
- return null;
7102
- };
7103
- //#endregion
7104
7508
  //#region src/plugin/utils/statement-always-exits.ts
7105
7509
  const statementAlwaysExits = (statement) => {
7106
7510
  if (isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement")) return true;
@@ -7146,8 +7550,8 @@ const applyDefinitions = (incomingDefinitions, definitions) => {
7146
7550
  const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow) => {
7147
7551
  if (!REASSIGNABLE_BINDING_KINDS.has(symbol.kind)) return symbol.initializer ? [symbol.initializer] : [];
7148
7552
  if (!controlFlow) return [];
7149
- const referenceFunction = findEnclosingFunction(referenceNode);
7150
- if (findEnclosingFunction(symbol.bindingIdentifier) !== referenceFunction) return [];
7553
+ const referenceFunction = findEnclosingFunction$1(referenceNode);
7554
+ if (findEnclosingFunction$1(symbol.bindingIdentifier) !== referenceFunction) return [];
7151
7555
  if (!referenceFunction) return [];
7152
7556
  const functionControlFlow = controlFlow.cfgFor(referenceFunction);
7153
7557
  if (!functionControlFlow) return [];
@@ -7171,7 +7575,7 @@ const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow)
7171
7575
  if (symbol.initializer) addDefinition(symbol.initializer, symbol.bindingIdentifier, bindingPosition);
7172
7576
  for (const reference of symbol.references) {
7173
7577
  const writePosition = getRangeStart(reference.identifier);
7174
- if (reference.flag === "read" || findEnclosingFunction(reference.identifier) !== referenceFunction || writePosition === null || writePosition >= referencePosition) continue;
7578
+ if (reference.flag === "read" || findEnclosingFunction$1(reference.identifier) !== referenceFunction || writePosition === null || writePosition >= referencePosition) continue;
7175
7579
  const assignedExpression = getAssignedExpressionForWrite(reference.identifier);
7176
7580
  if (!assignedExpression) continue;
7177
7581
  addDefinition(assignedExpression, reference.identifier, writePosition);
@@ -8725,7 +9129,7 @@ const getClassNameLiteral = (classAttribute) => {
8725
9129
  };
8726
9130
  //#endregion
8727
9131
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
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`.";
9132
+ 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`.";
8729
9133
  const NON_OPERABLE_ELEMENTS = new Set([
8730
9134
  "td",
8731
9135
  "th",
@@ -8843,6 +9247,7 @@ const HTML_FOR_ATTRIBUTE = "htmlFor";
8843
9247
  const LABEL_ELEMENT = "label";
8844
9248
  const LABEL_COMPONENT_NAME = "Label";
8845
9249
  const POLYMORPHIC_COMPONENT_PROP = "component";
9250
+ const TITLE_ATTRIBUTE = "title";
8846
9251
  const WRAPPER_LABEL_PROP = "label";
8847
9252
  const SELECT_ELEMENT = "select";
8848
9253
  const DEFAULT_DEPTH = 5;
@@ -8888,6 +9293,96 @@ const hasNonEmptyPropValue = (attribute) => {
8888
9293
  }
8889
9294
  return true;
8890
9295
  };
9296
+ const getLastJsxPropIgnoreCase = (attributes, targetProp) => {
9297
+ const targetPropLower = targetProp.toLowerCase();
9298
+ for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
9299
+ const attribute = attributes[attributeIndex];
9300
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute")) continue;
9301
+ if (getJsxAttributeName(attribute.name)?.toLowerCase() === targetPropLower) return attribute;
9302
+ }
9303
+ };
9304
+ const getStaticNativeTitleArrayValue = (expression, scopes) => {
9305
+ const elementValues = [];
9306
+ for (const rawElement of expression.elements) {
9307
+ if (rawElement === null) {
9308
+ elementValues.push("");
9309
+ continue;
9310
+ }
9311
+ if (isNodeOfType(rawElement, "SpreadElement")) return null;
9312
+ const element = stripParenExpression(rawElement);
9313
+ if (isNodeOfType(element, "Literal")) {
9314
+ elementValues.push(element.value === null ? "" : String(element.value));
9315
+ continue;
9316
+ }
9317
+ if (isNodeOfType(element, "TemplateLiteral")) {
9318
+ const staticValue = getStaticTemplateLiteralValue(element);
9319
+ if (staticValue === null) return null;
9320
+ elementValues.push(staticValue);
9321
+ continue;
9322
+ }
9323
+ if (isNodeOfType(element, "Identifier") && element.name === "undefined" && scopes.isGlobalReference(element) || isNodeOfType(element, "UnaryExpression") && element.operator === "void") {
9324
+ elementValues.push("");
9325
+ continue;
9326
+ }
9327
+ if (isNodeOfType(element, "ArrayExpression")) {
9328
+ const nestedValue = getStaticNativeTitleArrayValue(element, scopes);
9329
+ if (nestedValue === null) return null;
9330
+ elementValues.push(nestedValue);
9331
+ continue;
9332
+ }
9333
+ return null;
9334
+ }
9335
+ return elementValues.join(",");
9336
+ };
9337
+ const isGlobalSymbolExpression = (expression, scopes) => {
9338
+ if (isNodeOfType(expression, "Identifier")) return expression.name === "Symbol" && scopes.isGlobalReference(expression);
9339
+ if (!isNodeOfType(expression, "MemberExpression")) return false;
9340
+ const object = stripParenExpression(expression.object);
9341
+ return isNodeOfType(object, "Identifier") && object.name === "Symbol" && scopes.isGlobalReference(object);
9342
+ };
9343
+ const hasNonEmptyNativeTitleExpression = (rawExpression, scopes) => {
9344
+ const expression = stripParenExpression(rawExpression);
9345
+ if (isNodeOfType(expression, "Literal")) {
9346
+ if (typeof expression.value === "string") return expression.value.trim().length > 0;
9347
+ return expression.value !== null && typeof expression.value !== "boolean";
9348
+ }
9349
+ if (isNodeOfType(expression, "TemplateLiteral")) {
9350
+ const staticValue = getStaticTemplateLiteralValue(expression);
9351
+ return staticValue === null || staticValue.trim().length > 0;
9352
+ }
9353
+ if (isNodeOfType(expression, "Identifier")) return expression.name !== "undefined" || !scopes.isGlobalReference(expression);
9354
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return false;
9355
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return false;
9356
+ if (isNodeOfType(expression, "ArrowFunctionExpression")) return false;
9357
+ if (isNodeOfType(expression, "FunctionExpression")) return false;
9358
+ if (isNodeOfType(expression, "ClassExpression")) return false;
9359
+ if (isNodeOfType(expression, "ArrayExpression")) {
9360
+ const staticValue = getStaticNativeTitleArrayValue(expression, scopes);
9361
+ return staticValue === null || staticValue.trim().length > 0;
9362
+ }
9363
+ if (isGlobalSymbolExpression(expression, scopes)) return false;
9364
+ if (isNodeOfType(expression, "CallExpression") && isGlobalSymbolExpression(expression.callee, scopes)) return false;
9365
+ if (isNodeOfType(expression, "ConditionalExpression")) return hasNonEmptyNativeTitleExpression(expression.consequent, scopes) && hasNonEmptyNativeTitleExpression(expression.alternate, scopes);
9366
+ if (isNodeOfType(expression, "SequenceExpression")) {
9367
+ const finalExpression = expression.expressions.at(-1);
9368
+ return finalExpression ? hasNonEmptyNativeTitleExpression(finalExpression, scopes) : false;
9369
+ }
9370
+ if (isNodeOfType(expression, "LogicalExpression")) {
9371
+ const leftExpression = stripParenExpression(expression.left);
9372
+ if (!isNodeOfType(leftExpression, "Literal")) return expression.operator !== "&&";
9373
+ if (expression.operator === "??") return hasNonEmptyNativeTitleExpression(leftExpression.value === null ? expression.right : leftExpression, scopes);
9374
+ const leftValueIsTruthy = Boolean(leftExpression.value);
9375
+ if (expression.operator === "&&") return hasNonEmptyNativeTitleExpression(leftValueIsTruthy ? expression.right : leftExpression, scopes);
9376
+ return hasNonEmptyNativeTitleExpression(leftValueIsTruthy ? leftExpression : expression.right, scopes);
9377
+ }
9378
+ return true;
9379
+ };
9380
+ const hasNonEmptyNativeTitle = (attribute, scopes) => {
9381
+ if (!attribute?.value) return false;
9382
+ if (isNodeOfType(attribute.value, "Literal")) return typeof attribute.value.value === "string" && attribute.value.value.trim().length > 0;
9383
+ if (isNodeOfType(attribute.value, "JSXExpressionContainer")) return hasNonEmptyNativeTitleExpression(attribute.value.expression, scopes);
9384
+ return true;
9385
+ };
8891
9386
  const toAttributeMatchKey = (kind, value) => {
8892
9387
  const trimmedValue = value.trim();
8893
9388
  return trimmedValue.length > 0 ? `${kind}:${trimmedValue}` : null;
@@ -9144,6 +9639,7 @@ const controlHasAssociatedLabel = defineRule({
9144
9639
  if (typeValue === "submit" || typeValue === "reset") return;
9145
9640
  if (typeValue === "button" && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "value"))) return;
9146
9641
  }
9642
+ if (isDomElement && hasNonEmptyNativeTitle(getLastJsxPropIgnoreCase(opening.attributes, TITLE_ATTRIBUTE), context.scopes)) return;
9147
9643
  if (supportsPlaceholderNameFallback(tagName, opening) && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "placeholder"))) return;
9148
9644
  if (hasLabellingProp(opening.attributes, settings.labelAttributes)) return;
9149
9645
  if (isInsideJsxAttribute(node)) return;
@@ -9163,7 +9659,7 @@ const controlHasAssociatedLabel = defineRule({
9163
9659
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
9164
9660
  context.report({
9165
9661
  node: candidate.opening,
9166
- message: MESSAGE$56
9662
+ message: MESSAGE$57
9167
9663
  });
9168
9664
  }
9169
9665
  }
@@ -9924,7 +10420,7 @@ const noVagueButtonLabel = defineRule({
9924
10420
  });
9925
10421
  //#endregion
9926
10422
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
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.";
10423
+ 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.";
9928
10424
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
9929
10425
  const NAME_PROVIDING_ATTRIBUTES = [
9930
10426
  "aria-label",
@@ -9949,7 +10445,7 @@ const dialogHasAccessibleName = defineRule({
9949
10445
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
9950
10446
  context.report({
9951
10447
  node: node.name,
9952
- message: MESSAGE$55
10448
+ message: MESSAGE$56
9953
10449
  });
9954
10450
  } };
9955
10451
  }
@@ -9989,7 +10485,7 @@ const isEs6Component = (node) => {
9989
10485
  };
9990
10486
  //#endregion
9991
10487
  //#region src/plugin/rules/react-builtins/display-name.ts
9992
- const MESSAGE$54 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
10488
+ const MESSAGE$55 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
9993
10489
  const DEFAULT_ADDITIONAL_HOCS = [
9994
10490
  "observer",
9995
10491
  "lazy",
@@ -10182,7 +10678,7 @@ const displayName = defineRule({
10182
10678
  const reportAt = (node) => {
10183
10679
  context.report({
10184
10680
  node,
10185
- message: MESSAGE$54
10681
+ message: MESSAGE$55
10186
10682
  });
10187
10683
  };
10188
10684
  return {
@@ -11093,7 +11589,7 @@ const componentOrHookDisplayNameForFunction = (functionNode) => {
11093
11589
  //#endregion
11094
11590
  //#region src/plugin/utils/enclosing-component-or-hook-name.ts
11095
11591
  const enclosingComponentOrHookName = (node) => {
11096
- const functionNode = findEnclosingFunction(node);
11592
+ const functionNode = findEnclosingFunction$1(node);
11097
11593
  return functionNode ? componentOrHookDisplayNameForFunction(functionNode) : null;
11098
11594
  };
11099
11595
  //#endregion
@@ -11150,11 +11646,11 @@ const executesDuringRender = (functionNode, scopes) => {
11150
11646
  //#endregion
11151
11647
  //#region src/plugin/utils/find-render-phase-component-or-hook.ts
11152
11648
  const findRenderPhaseComponentOrHook = (node, scopes) => {
11153
- let functionNode = findEnclosingFunction(node);
11649
+ let functionNode = findEnclosingFunction$1(node);
11154
11650
  while (functionNode) {
11155
11651
  if (componentOrHookDisplayNameForFunction(functionNode)) return functionNode;
11156
11652
  if (!executesDuringRender(functionNode, scopes)) return null;
11157
- functionNode = findEnclosingFunction(functionNode);
11653
+ functionNode = findEnclosingFunction$1(functionNode);
11158
11654
  }
11159
11655
  return null;
11160
11656
  };
@@ -11229,6 +11725,50 @@ const isCleanupReturningSubscribeLikeCallExpression = (node) => {
11229
11725
  return true;
11230
11726
  };
11231
11727
  //#endregion
11728
+ //#region src/plugin/utils/is-node-reachable-within-function.ts
11729
+ const isInsideStaticallyUnreachableBranch = (node) => {
11730
+ let child = node;
11731
+ let parent = node.parent;
11732
+ while (parent) {
11733
+ if (isNodeOfType(parent, "IfStatement") && isNodeOfType(parent.test, "Literal")) {
11734
+ if (parent.test.value === false && parent.consequent === child) return true;
11735
+ if (parent.test.value === true && parent.alternate === child) return true;
11736
+ }
11737
+ if (isNodeOfType(parent, "ConditionalExpression") && isNodeOfType(parent.test, "Literal")) {
11738
+ if (parent.test.value === false && parent.consequent === child) return true;
11739
+ if (parent.test.value === true && parent.alternate === child) return true;
11740
+ }
11741
+ if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) {
11742
+ if (isNodeOfType(parent.left, "Literal") && (parent.operator === "&&" && !parent.left.value || parent.operator === "||" && Boolean(parent.left.value))) return true;
11743
+ }
11744
+ child = parent;
11745
+ parent = parent.parent;
11746
+ }
11747
+ return false;
11748
+ };
11749
+ const isNodeReachableWithinFunction = (node, context) => {
11750
+ if (isInsideStaticallyUnreachableBranch(node)) return false;
11751
+ const owner = context.cfg.enclosingFunction(node);
11752
+ if (!owner) return true;
11753
+ const functionCfg = context.cfg.cfgFor(owner);
11754
+ if (!functionCfg) return true;
11755
+ const targetBlock = functionCfg.blockOf(node);
11756
+ if (!targetBlock) return true;
11757
+ const visitedBlocks = new Set([functionCfg.entry]);
11758
+ const pendingBlocks = [functionCfg.entry];
11759
+ while (pendingBlocks.length > 0) {
11760
+ const currentBlock = pendingBlocks.pop();
11761
+ if (!currentBlock) break;
11762
+ if (currentBlock === targetBlock) return true;
11763
+ for (const edge of currentBlock.successors) {
11764
+ if (visitedBlocks.has(edge.to)) continue;
11765
+ visitedBlocks.add(edge.to);
11766
+ pendingBlocks.push(edge.to);
11767
+ }
11768
+ }
11769
+ return false;
11770
+ };
11771
+ //#endregion
11232
11772
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
11233
11773
  const OBSERVER_REGISTRATION_METHOD_NAME = "observe";
11234
11774
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
@@ -11357,36 +11897,15 @@ const findSubscribeLikeUsages = (callback, context) => {
11357
11897
  });
11358
11898
  return usages.filter((usage) => isNodeReachableWithinFunction(usage.node, context));
11359
11899
  };
11360
- const isNodeReachableWithinFunction = (node, context) => {
11361
- const owner = context.cfg.enclosingFunction(node);
11362
- if (!owner) return true;
11363
- const functionCfg = context.cfg.cfgFor(owner);
11364
- if (!functionCfg) return true;
11365
- const targetBlock = functionCfg.blockOf(node);
11366
- if (!targetBlock) return true;
11367
- const visitedBlocks = new Set([functionCfg.entry]);
11368
- const pendingBlocks = [functionCfg.entry];
11369
- while (pendingBlocks.length > 0) {
11370
- const currentBlock = pendingBlocks.pop();
11371
- if (!currentBlock) break;
11372
- if (currentBlock === targetBlock) return true;
11373
- for (const edge of currentBlock.successors) {
11374
- if (visitedBlocks.has(edge.to)) continue;
11375
- visitedBlocks.add(edge.to);
11376
- pendingBlocks.push(edge.to);
11377
- }
11378
- }
11379
- return false;
11380
- };
11381
11900
  const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, context) => {
11382
11901
  let pathAnchor = usageNode;
11383
- let pathOwner = findEnclosingFunction(pathAnchor);
11902
+ let pathOwner = findEnclosingFunction$1(pathAnchor);
11384
11903
  while (pathOwner && isSynchronousIteratorCallback(pathOwner)) {
11385
11904
  if (matchingNodes.length > 0 && matchingNodes.every((matchingNode) => context.cfg.enclosingFunction(matchingNode) === pathOwner)) break;
11386
11905
  const iteratorCall = pathOwner.parent;
11387
11906
  if (!isNodeOfType(iteratorCall, "CallExpression")) break;
11388
11907
  pathAnchor = iteratorCall;
11389
- pathOwner = findEnclosingFunction(pathAnchor);
11908
+ pathOwner = findEnclosingFunction$1(pathAnchor);
11390
11909
  }
11391
11910
  const owner = context.cfg.enclosingFunction(pathAnchor);
11392
11911
  if (!owner) return false;
@@ -11615,7 +12134,7 @@ const findCollectionMappingCall = (callbackNode) => {
11615
12134
  return callee.property.name === "map" && callNode.arguments?.[0] === callbackNode ? callNode : null;
11616
12135
  };
11617
12136
  const findMappedResourceCollectionKey = (resourceNode, context) => {
11618
- const callbackNode = findEnclosingFunction(resourceNode);
12137
+ const callbackNode = findEnclosingFunction$1(resourceNode);
11619
12138
  if (!callbackNode || !isNodeOfType(callbackNode, "ArrowFunctionExpression") && !isNodeOfType(callbackNode, "FunctionExpression")) return null;
11620
12139
  const mappingCall = findCollectionMappingCall(callbackNode);
11621
12140
  if (!mappingCall) return null;
@@ -11726,10 +12245,10 @@ const findSingleDirectInvocation = (functionNode, caller, context) => {
11726
12245
  });
11727
12246
  if (invocationCalls.length !== 1) return null;
11728
12247
  const invocationCall = invocationCalls[0];
11729
- return findEnclosingFunction(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
12248
+ return findEnclosingFunction$1(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
11730
12249
  };
11731
12250
  const resolveCleanupPathAnchor = (usageNode, effectCallback, context) => {
11732
- const usageFunction = findEnclosingFunction(usageNode);
12251
+ const usageFunction = findEnclosingFunction$1(usageNode);
11733
12252
  if (!usageFunction || usageFunction === effectCallback) return usageNode;
11734
12253
  return findSingleDirectInvocation(usageFunction, effectCallback, context) ?? usageNode;
11735
12254
  };
@@ -11744,7 +12263,7 @@ const resolveSingleAssignedCleanupFunction = (expression, usage, context) => {
11744
12263
  const assignmentReference = assignmentReferences[0];
11745
12264
  const assignmentTarget = findTransparentExpressionRoot(assignmentReference.identifier);
11746
12265
  const assignmentNode = assignmentTarget.parent;
11747
- if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction(assignmentNode) !== findEnclosingFunction(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [assignmentNode], context)) return null;
12266
+ if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction$1(assignmentNode) !== findEnclosingFunction$1(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [assignmentNode], context)) return null;
11748
12267
  const assignedValue = stripParenExpression(assignmentNode.right);
11749
12268
  return isFunctionLike$2(assignedValue) ? assignedValue : null;
11750
12269
  };
@@ -11833,12 +12352,12 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
11833
12352
  return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
11834
12353
  };
11835
12354
  const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
11836
- const componentFunction = findEnclosingFunction(callback);
12355
+ const componentFunction = findEnclosingFunction$1(callback);
11837
12356
  if (!componentFunction || !isNodeOfType(componentFunction, "ArrowFunctionExpression") && !isNodeOfType(componentFunction, "FunctionExpression") && !isNodeOfType(componentFunction, "FunctionDeclaration")) return false;
11838
12357
  let didFindUnmountCleanup = false;
11839
12358
  walkAst(componentFunction.body, (child) => {
11840
12359
  if (didFindUnmountCleanup) return false;
11841
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction) return;
12360
+ if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction) return;
11842
12361
  if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
11843
12362
  const dependencyList = child.arguments?.[1];
11844
12363
  if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
@@ -11934,7 +12453,7 @@ const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, co
11934
12453
  return didWriteGuard;
11935
12454
  };
11936
12455
  const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
11937
- const usageFunction = findEnclosingFunction(usage.node);
12456
+ const usageFunction = findEnclosingFunction$1(usage.node);
11938
12457
  const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
11939
12458
  if (usage.handleKey === null || !usageFunction || usageFunction === callback || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
11940
12459
  return collectDeferredUsageGuardStates(usageFunction, usage.node, context).some((guardState) => !deferredUsageWritesGuardBeforeUsage(usageFunction, usage.node, guardState, context) && cleanupReturns.every((cleanupReturn) => cleanupReturnInvalidatesGuard(cleanupReturn, guardState, context)));
@@ -11942,7 +12461,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
11942
12461
  const effectHasCleanupForUsage = (callback, usage, context) => {
11943
12462
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
11944
12463
  if (callback.async) return false;
11945
- if (usage.kind === "subscribe" && findEnclosingFunction(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
12464
+ if (usage.kind === "subscribe" && findEnclosingFunction$1(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
11946
12465
  if (!isNodeOfType(callback.body, "BlockStatement")) return callback.body === usage.node && isCleanupReturningSubscribeLikeCallExpression(callback.body);
11947
12466
  const matchingCleanupReturns = [];
11948
12467
  walkInsideStatementBlocks(callback.body, (child) => {
@@ -12111,7 +12630,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
12111
12630
  parentNode = currentNode.parent;
12112
12631
  }
12113
12632
  if (!isNodeOfType(parentNode, "ReturnStatement") || parentNode.argument !== currentNode) return false;
12114
- const effectCallback = findEnclosingFunction(parentNode);
12633
+ const effectCallback = findEnclosingFunction$1(parentNode);
12115
12634
  const effectCall = effectCallback?.parent;
12116
12635
  return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
12117
12636
  };
@@ -12121,13 +12640,13 @@ const isPotentiallyReachableFunction = (functionNode, context) => {
12121
12640
  if (!bindingIdentifier) return false;
12122
12641
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12123
12642
  if (!symbol) return false;
12124
- return symbol.references.some((reference) => findEnclosingFunction(reference.identifier) !== functionNode);
12643
+ return symbol.references.some((reference) => findEnclosingFunction$1(reference.identifier) !== functionNode);
12125
12644
  };
12126
12645
  const isReleaseReachableForUsage = (releaseNode, usage, context) => {
12127
12646
  if (!isNodeReachableWithinFunction(releaseNode, context)) return false;
12128
- const releaseFunction = findEnclosingFunction(releaseNode);
12647
+ const releaseFunction = findEnclosingFunction$1(releaseNode);
12129
12648
  if (!releaseFunction) return true;
12130
- if (releaseFunction === findEnclosingFunction(usage.node)) return true;
12649
+ if (releaseFunction === findEnclosingFunction$1(usage.node)) return true;
12131
12650
  return isPotentiallyReachableFunction(releaseFunction, context);
12132
12651
  };
12133
12652
  const fileContainsReleaseForUsage = (usage, context) => {
@@ -12310,10 +12829,10 @@ const cleanupFunctionReleasesRefOwnedUsage = (cleanupFunction, componentFunction
12310
12829
  }
12311
12830
  if (assignedKey !== storage.refCurrentKey) return;
12312
12831
  const assignedValue = stripParenExpression(child.right);
12313
- if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction(child) === cleanupFunction) return;
12832
+ if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction$1(child) === cleanupFunction) return;
12314
12833
  const assignedSessionProperties = (isNodeOfType(assignedValue, "ObjectExpression") ? assignedValue : null)?.properties ?? [];
12315
12834
  const storesMatchingHandler = assignedSessionProperties.every((property) => isNodeOfType(property, "Property")) && assignedSessionProperties.some((property) => isNodeOfType(property, "Property") && `${storage.refCurrentKey}.${getStaticPropertyKeyName(property) ?? ""}` === storage.handlerKey && resolveExpressionKey(property.value, context) === usage.handlerKey);
12316
- if (findEnclosingFunction(child) !== retainedFunction || !storesMatchingHandler) {
12835
+ if (findEnclosingFunction$1(child) !== retainedFunction || !storesMatchingHandler) {
12317
12836
  hasUnsafeRefWrite = true;
12318
12837
  return false;
12319
12838
  }
@@ -12334,12 +12853,12 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
12334
12853
  return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
12335
12854
  };
12336
12855
  const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
12337
- const componentFunction = findEnclosingFunction(retainedFunction);
12856
+ const componentFunction = findEnclosingFunction$1(retainedFunction);
12338
12857
  if (!componentFunction || !isFunctionLike$2(componentFunction)) return false;
12339
12858
  let didFindCleanupEffect = false;
12340
12859
  walkAst(componentFunction.body, (child) => {
12341
12860
  if (didFindCleanupEffect) return false;
12342
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction || !isReactApiCall(child, "useEffect", context.scopes)) return;
12861
+ if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction || !isReactApiCall(child, "useEffect", context.scopes)) return;
12343
12862
  const effectStatement = findTransparentExpressionRoot(child).parent;
12344
12863
  if (!isNodeOfType(effectStatement, "ExpressionStatement") || effectStatement.parent !== componentFunction.body) return;
12345
12864
  const effectCallback = getEffectCallback(child);
@@ -13193,6 +13712,202 @@ const resolveExhaustiveDepsSettings = (settings) => {
13193
13712
  };
13194
13713
  };
13195
13714
  //#endregion
13715
+ //#region src/plugin/utils/is-ast-descendant.ts
13716
+ /**
13717
+ * True when `inner` is `outer` itself or any descendant in the AST
13718
+ * `parent` chain. Walks `inner.parent` upward and stops at either a
13719
+ * match (`true`) or the chain's root (`false`).
13720
+ *
13721
+ * Was duplicated byte-identical in:
13722
+ * - exhaustive-deps-symbol-stability.ts
13723
+ * - semantic/closure-captures.ts
13724
+ */
13725
+ const isAstDescendant = (inner, outer) => {
13726
+ let current = inner;
13727
+ while (current) {
13728
+ if (current === outer) return true;
13729
+ current = current.parent ?? null;
13730
+ }
13731
+ return false;
13732
+ };
13733
+ //#endregion
13734
+ //#region src/plugin/rules/react-builtins/exhaustive-deps-sole-writer-guard.ts
13735
+ const EQUALITY_BINARY_OPERATORS = new Set(["===", "!=="]);
13736
+ const getUseStateSetterSymbol = (stateSymbol, scopes) => {
13737
+ const declaration = stateSymbol.declarationNode;
13738
+ if (!isNodeOfType(declaration, "VariableDeclarator")) return null;
13739
+ if (!isNodeOfType(declaration.id, "ArrayPattern")) return null;
13740
+ const stateBinding = declaration.id.elements[0];
13741
+ const setterBinding = declaration.id.elements[1];
13742
+ if (stateBinding !== stateSymbol.bindingIdentifier || !isNodeOfType(setterBinding, "Identifier")) return null;
13743
+ const initializer = declaration.init ? stripParenExpression(declaration.init) : null;
13744
+ if (!initializer || !isReactApiCall(initializer, "useState", scopes, {
13745
+ allowGlobalReactNamespace: true,
13746
+ allowUnboundBareCalls: true,
13747
+ resolveNamedAliases: true
13748
+ })) return null;
13749
+ return scopes.symbolFor(setterBinding);
13750
+ };
13751
+ const getDirectSetterCall = (setterSymbol, callback) => {
13752
+ if (setterSymbol.references.length !== 1) return null;
13753
+ const setterReference = setterSymbol.references[0];
13754
+ if (setterReference.flag !== "read") return null;
13755
+ const referenceRoot = findTransparentExpressionRoot(setterReference.identifier);
13756
+ const callExpression = referenceRoot.parent;
13757
+ if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== referenceRoot || callExpression.arguments.length !== 1 || findEnclosingFunction$1(callExpression) !== callback) return null;
13758
+ const writtenValue = stripParenExpression(callExpression.arguments[0]);
13759
+ if (isNodeOfType(writtenValue, "ArrowFunctionExpression") || isNodeOfType(writtenValue, "FunctionExpression") || isNodeOfType(writtenValue, "SpreadElement")) return null;
13760
+ return {
13761
+ callExpression,
13762
+ writtenValue
13763
+ };
13764
+ };
13765
+ const isGlobalObjectIsCall = (callExpression, scopes) => {
13766
+ if (!isNodeOfType(callExpression, "CallExpression")) return false;
13767
+ const callee = stripParenExpression(callExpression.callee);
13768
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
13769
+ const propertyName = getStaticPropertyName(callee);
13770
+ const receiver = stripParenExpression(callee.object);
13771
+ return Boolean(propertyName === "is" && isNodeOfType(receiver, "Identifier") && receiver.name === "Object" && scopes.isGlobalReference(receiver));
13772
+ };
13773
+ const isGlobalNaNReference = (expression, scopes) => {
13774
+ const unwrappedExpression = stripParenExpression(expression);
13775
+ return isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression);
13776
+ };
13777
+ const getEqualityComparison = (stateReference, candidate, scopes) => {
13778
+ const unwrappedStateReference = stripParenExpression(stateReference);
13779
+ if (isNodeOfType(candidate, "BinaryExpression") && EQUALITY_BINARY_OPERATORS.has(candidate.operator)) {
13780
+ if (stripParenExpression(candidate.left) === unwrappedStateReference) return isGlobalNaNReference(candidate.right, scopes) ? null : {
13781
+ comparison: candidate,
13782
+ counterpart: candidate.right,
13783
+ areValuesEqualWhenTruthy: candidate.operator === "==="
13784
+ };
13785
+ if (stripParenExpression(candidate.right) === unwrappedStateReference) return isGlobalNaNReference(candidate.left, scopes) ? null : {
13786
+ comparison: candidate,
13787
+ counterpart: candidate.left,
13788
+ areValuesEqualWhenTruthy: candidate.operator === "==="
13789
+ };
13790
+ return null;
13791
+ }
13792
+ if (!isNodeOfType(candidate, "CallExpression") || !isGlobalObjectIsCall(candidate, scopes) || candidate.arguments.length !== 2) return null;
13793
+ const firstArgument = candidate.arguments[0];
13794
+ const secondArgument = candidate.arguments[1];
13795
+ if (isNodeOfType(firstArgument, "SpreadElement") || isNodeOfType(secondArgument, "SpreadElement")) return null;
13796
+ if (stripParenExpression(firstArgument) === unwrappedStateReference) return {
13797
+ comparison: candidate,
13798
+ counterpart: secondArgument,
13799
+ areValuesEqualWhenTruthy: true
13800
+ };
13801
+ if (stripParenExpression(secondArgument) === unwrappedStateReference) return {
13802
+ comparison: candidate,
13803
+ counterpart: firstArgument,
13804
+ areValuesEqualWhenTruthy: true
13805
+ };
13806
+ return null;
13807
+ };
13808
+ const findEqualityComparison = (stateReference, test, scopes) => {
13809
+ let current = stateReference.parent;
13810
+ while (current && isAstDescendant(current, test)) {
13811
+ const comparison = getEqualityComparison(stateReference, current, scopes);
13812
+ if (comparison) return comparison;
13813
+ if (current === test) break;
13814
+ current = current.parent;
13815
+ }
13816
+ return null;
13817
+ };
13818
+ const doesTestOutcomeRequireComparisonOutcome = (comparison, test, testOutcome, comparisonOutcome) => {
13819
+ let requiredChildOutcome = testOutcome;
13820
+ let current = comparison;
13821
+ while (current !== test) {
13822
+ const parent = current.parent;
13823
+ if (!parent || !isAstDescendant(parent, test)) return false;
13824
+ if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") requiredChildOutcome = !requiredChildOutcome;
13825
+ else if (isNodeOfType(parent, "LogicalExpression")) {
13826
+ if (parent.operator === "&&" && !requiredChildOutcome) return false;
13827
+ if (parent.operator === "||" && requiredChildOutcome) return false;
13828
+ if (parent.operator !== "&&" && parent.operator !== "||") return false;
13829
+ } else if (!TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type)) return false;
13830
+ current = parent;
13831
+ }
13832
+ return requiredChildOutcome === comparisonOutcome;
13833
+ };
13834
+ const resolveImmutableAliasExpression = (expression, scopes) => {
13835
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
13836
+ let current = stripParenExpression(expression);
13837
+ while (isNodeOfType(current, "Identifier")) {
13838
+ const symbol = scopes.symbolFor(current);
13839
+ if (!symbol || visitedSymbolIds.has(symbol.id)) break;
13840
+ const initializer = getDirectConstInitializer(symbol);
13841
+ if (!initializer || !isNodeOfType(stripParenExpression(initializer), "Identifier")) break;
13842
+ visitedSymbolIds.add(symbol.id);
13843
+ current = stripParenExpression(initializer);
13844
+ }
13845
+ return current;
13846
+ };
13847
+ const referencesSameValue = (leftExpression, rightExpression, scopes) => {
13848
+ const left = resolveImmutableAliasExpression(leftExpression, scopes);
13849
+ const right = resolveImmutableAliasExpression(rightExpression, scopes);
13850
+ if (left === right) return true;
13851
+ if (isNodeOfType(left, "Identifier") && isNodeOfType(right, "Identifier")) {
13852
+ const leftSymbol = scopes.symbolFor(left);
13853
+ const rightSymbol = scopes.symbolFor(right);
13854
+ if (leftSymbol || rightSymbol) return leftSymbol?.id === rightSymbol?.id;
13855
+ return left.name === right.name;
13856
+ }
13857
+ if (isNodeOfType(left, "Literal") && isNodeOfType(right, "Literal")) return Object.is(left.value, right.value);
13858
+ return false;
13859
+ };
13860
+ const hasDeclaredTriggerDependency = (callback) => {
13861
+ const hookCall = findTransparentExpressionRoot(callback).parent;
13862
+ if (!isNodeOfType(hookCall, "CallExpression")) return false;
13863
+ const dependencyArray = hookCall.arguments[1];
13864
+ return Boolean(isNodeOfType(dependencyArray, "ArrayExpression") && dependencyArray.elements.length);
13865
+ };
13866
+ const doesBranchExitEffect = (branch) => {
13867
+ if (isNodeOfType(branch, "ReturnStatement") || isNodeOfType(branch, "ThrowStatement")) return true;
13868
+ if (!isNodeOfType(branch, "BlockStatement")) return false;
13869
+ const terminalStatement = branch.body.at(-1);
13870
+ return Boolean(terminalStatement && doesBranchExitEffect(terminalStatement));
13871
+ };
13872
+ const doesGuardDominateLaterSetter = (guard, setterCall) => {
13873
+ if (!isNodeOfType(guard, "IfStatement") || guard.alternate) return false;
13874
+ if (!doesBranchExitEffect(guard.consequent)) return false;
13875
+ const block = guard.parent;
13876
+ if (!isNodeOfType(block, "BlockStatement")) return false;
13877
+ const guardIndex = block.body.findIndex((statement) => statement === guard);
13878
+ return block.body.some((statement, statementIndex) => statementIndex > guardIndex && isAstDescendant(setterCall, statement));
13879
+ };
13880
+ const findDominatingGuardCounterpart = (stateReference, setterCall, callback, scopes) => {
13881
+ let current = stateReference.parent;
13882
+ while (current && current !== callback) {
13883
+ if (isNodeOfType(current, "IfStatement") && isAstDescendant(stateReference, current.test) && (isAstDescendant(setterCall, current.consequent) || Boolean(current.alternate && isAstDescendant(setterCall, current.alternate)) || doesGuardDominateLaterSetter(current, setterCall))) {
13884
+ const setterRunsWhenTestTruthy = isAstDescendant(setterCall, current.consequent);
13885
+ if (setterRunsWhenTestTruthy === (Boolean(current.alternate && isAstDescendant(setterCall, current.alternate)) || doesGuardDominateLaterSetter(current, setterCall))) return null;
13886
+ const equalityComparison = findEqualityComparison(stateReference, current.test, scopes);
13887
+ if (!equalityComparison) return null;
13888
+ const comparisonOutcomeForDifferentValues = !equalityComparison.areValuesEqualWhenTruthy;
13889
+ if (!doesTestOutcomeRequireComparisonOutcome(equalityComparison.comparison, current.test, setterRunsWhenTestTruthy, comparisonOutcomeForDifferentValues)) return null;
13890
+ return equalityComparison.counterpart;
13891
+ }
13892
+ current = current.parent;
13893
+ }
13894
+ return null;
13895
+ };
13896
+ const isSoleWriterEffectGuardCapture = (stateSymbol, callback, scopes) => {
13897
+ if (!hasDeclaredTriggerDependency(callback)) return false;
13898
+ if (stateSymbol.references.some((reference) => reference.flag !== "read")) return false;
13899
+ const setterSymbol = getUseStateSetterSymbol(stateSymbol, scopes);
13900
+ if (!setterSymbol) return false;
13901
+ const setterWrite = getDirectSetterCall(setterSymbol, callback);
13902
+ if (!setterWrite) return false;
13903
+ const callbackStateReferences = stateSymbol.references.filter((reference) => isAstDescendant(reference.identifier, callback));
13904
+ if (callbackStateReferences.length !== 1) return false;
13905
+ const stateReferenceRoot = findTransparentExpressionRoot(callbackStateReferences[0].identifier);
13906
+ if (isNodeOfType(stateReferenceRoot.parent, "MemberExpression") && stateReferenceRoot.parent.object === stateReferenceRoot) return false;
13907
+ const counterpart = findDominatingGuardCounterpart(callbackStateReferences[0].identifier, setterWrite.callExpression, callback, scopes);
13908
+ return Boolean(counterpart && referencesSameValue(counterpart, setterWrite.writtenValue, scopes));
13909
+ };
13910
+ //#endregion
13196
13911
  //#region src/plugin/rules/react-builtins/exhaustive-deps-suppression.ts
13197
13912
  const DISABLE_COMMENT_RULE_NAME_PATTERN$1 = /(?:^|[\s,/])exhaustive-deps(?:$|[\s,:])/;
13198
13913
  const DISABLE_NEXT_LINE_PATTERN$1 = /\b(?:eslint|oxlint)-disable-next-line\b([^\n]*)/;
@@ -13264,25 +13979,6 @@ const isExhaustiveDepsSuppressedAt = (filename, nodeStartOffset) => {
13264
13979
  return index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf16NewlineOffsets)) || index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf8NewlineOffsets));
13265
13980
  };
13266
13981
  //#endregion
13267
- //#region src/plugin/utils/is-ast-descendant.ts
13268
- /**
13269
- * True when `inner` is `outer` itself or any descendant in the AST
13270
- * `parent` chain. Walks `inner.parent` upward and stops at either a
13271
- * match (`true`) or the chain's root (`false`).
13272
- *
13273
- * Was duplicated byte-identical in:
13274
- * - exhaustive-deps-symbol-stability.ts
13275
- * - semantic/closure-captures.ts
13276
- */
13277
- const isAstDescendant = (inner, outer) => {
13278
- let current = inner;
13279
- while (current) {
13280
- if (current === outer) return true;
13281
- current = current.parent ?? null;
13282
- }
13283
- return false;
13284
- };
13285
- //#endregion
13286
13982
  //#region src/plugin/rules/react-builtins/exhaustive-deps-symbol-stability.ts
13287
13983
  /**
13288
13984
  * Symbol-stability helpers consumed by the `exhaustive-deps` rule.
@@ -13463,6 +14159,7 @@ const EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS = new Set([
13463
14159
  "useLayoutEffect",
13464
14160
  "useInsertionEffect"
13465
14161
  ]);
14162
+ const SOLE_WRITER_GUARD_HOOKS = new Set(["useEffect", "useLayoutEffect"]);
13466
14163
  const buildAdditionalHooksRegex = (additional) => {
13467
14164
  if (!additional) return null;
13468
14165
  try {
@@ -13599,7 +14296,7 @@ const stringifyMemberChain = (node) => {
13599
14296
  }
13600
14297
  return null;
13601
14298
  };
13602
- const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys) => {
14299
+ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys, allowSoleWriterEffectGuards = false) => {
13603
14300
  const keys = /* @__PURE__ */ new Set();
13604
14301
  const stableCapturedNames = /* @__PURE__ */ new Set();
13605
14302
  const moduleScopeCapturedNames = /* @__PURE__ */ new Set();
@@ -13610,6 +14307,10 @@ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys) => {
13610
14307
  const symbol = reference.resolvedSymbol;
13611
14308
  if (!symbol) continue;
13612
14309
  if (isRecursiveInitializerCapture(symbol, callback)) continue;
14310
+ if (allowSoleWriterEffectGuards && isSoleWriterEffectGuardCapture(symbol, callback, scopes)) {
14311
+ stableCapturedNames.add(symbol.name);
14312
+ continue;
14313
+ }
13613
14314
  if (symbolHasStableValue(symbol, scopes)) {
13614
14315
  stableCapturedNames.add(symbol.name);
13615
14316
  continue;
@@ -14282,7 +14983,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
14282
14983
  if (isNodeOfType(stripped, "Identifier")) declaredExactBindingKeys.add(key);
14283
14984
  declaredKeyToReportNode.set(key, elementNode);
14284
14985
  }
14285
- const { keys: captureKeys, stableCapturedNames, moduleScopeCapturedNames, outerFunctionCapturedNames } = collectCaptureDepKeys(callbackToAnalyze ?? callbackArgument, context.scopes, declaredExactBindingKeys);
14986
+ const { keys: captureKeys, stableCapturedNames, moduleScopeCapturedNames, outerFunctionCapturedNames } = collectCaptureDepKeys(callbackToAnalyze ?? callbackArgument, context.scopes, declaredExactBindingKeys, SOLE_WRITER_GUARD_HOOKS.has(hookName));
14286
14987
  for (const forcedCaptureKey of forcedCaptureKeys) captureKeys.add(forcedCaptureKey);
14287
14988
  addAggregatePropsDependency(captureKeys, declaredKeys, callbackToAnalyze ?? callbackArgument, context.scopes);
14288
14989
  const missingCaptureKeys = [];
@@ -14722,7 +15423,7 @@ const forbidElements = defineRule({
14722
15423
  });
14723
15424
  //#endregion
14724
15425
  //#region src/plugin/rules/react-builtins/forward-ref-uses-ref.ts
14725
- const MESSAGE$53 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
15426
+ const MESSAGE$54 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
14726
15427
  const forwardRefUsesRef = defineRule({
14727
15428
  id: "forward-ref-uses-ref",
14728
15429
  title: "forwardRef without ref parameter",
@@ -14745,7 +15446,7 @@ const forwardRefUsesRef = defineRule({
14745
15446
  if (isNodeOfType(onlyParam, "RestElement")) return;
14746
15447
  context.report({
14747
15448
  node: inner,
14748
- message: MESSAGE$53
15449
+ message: MESSAGE$54
14749
15450
  });
14750
15451
  } })
14751
15452
  });
@@ -14786,7 +15487,7 @@ const gitProviderUrlInjectionRisk = defineRule({
14786
15487
  });
14787
15488
  //#endregion
14788
15489
  //#region src/plugin/rules/a11y/heading-has-content.ts
14789
- 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`.";
15490
+ 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`.";
14790
15491
  const DEFAULT_HEADING_TAGS = [
14791
15492
  "h1",
14792
15493
  "h2",
@@ -14820,7 +15521,7 @@ const headingHasContent = defineRule({
14820
15521
  for (const attribute of ["aria-label", "aria-labelledby"]) if (hasJsxPropIgnoreCase(node.attributes, attribute)) return;
14821
15522
  context.report({
14822
15523
  node,
14823
- message: MESSAGE$52
15524
+ message: MESSAGE$53
14824
15525
  });
14825
15526
  } };
14826
15527
  }
@@ -14984,7 +15685,7 @@ const hooksNoNanInDeps = defineRule({
14984
15685
  });
14985
15686
  //#endregion
14986
15687
  //#region src/plugin/rules/a11y/html-has-lang.ts
14987
- const MESSAGE$51 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
15688
+ const MESSAGE$52 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
14988
15689
  const resolveSettings$39 = (settings) => {
14989
15690
  const reactDoctor = settings?.["react-doctor"];
14990
15691
  return { htmlTags: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.htmlHasLang ?? {} : {}).htmlTags ?? ["html"] };
@@ -15031,13 +15732,13 @@ const htmlHasLang = defineRule({
15031
15732
  if (!lang) {
15032
15733
  context.report({
15033
15734
  node: node.name,
15034
- message: MESSAGE$51
15735
+ message: MESSAGE$52
15035
15736
  });
15036
15737
  return;
15037
15738
  }
15038
15739
  if (evaluateLang(lang.value) === "empty") context.report({
15039
15740
  node: lang,
15040
- message: MESSAGE$51
15741
+ message: MESSAGE$52
15041
15742
  });
15042
15743
  } };
15043
15744
  }
@@ -15277,7 +15978,7 @@ const htmlNoNestedInteractive = defineRule({
15277
15978
  });
15278
15979
  //#endregion
15279
15980
  //#region src/plugin/rules/a11y/iframe-has-title.ts
15280
- const MESSAGE$50 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15981
+ const MESSAGE$51 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15281
15982
  const evaluateTitleValue = (value) => {
15282
15983
  if (!value) return "missing";
15283
15984
  if (isNodeOfType(value, "Literal")) {
@@ -15317,14 +16018,14 @@ const iframeHasTitle = defineRule({
15317
16018
  if (!titleAttr) {
15318
16019
  if (hasSpread || tag === "iframe") context.report({
15319
16020
  node: node.name,
15320
- message: MESSAGE$50
16021
+ message: MESSAGE$51
15321
16022
  });
15322
16023
  return;
15323
16024
  }
15324
16025
  const verdict = evaluateTitleValue(titleAttr.value);
15325
16026
  if (verdict === "missing" || verdict === "empty") context.report({
15326
16027
  node: titleAttr,
15327
- message: MESSAGE$50
16028
+ message: MESSAGE$51
15328
16029
  });
15329
16030
  } })
15330
16031
  });
@@ -15449,7 +16150,7 @@ const iframeMissingSandbox = defineRule({
15449
16150
  });
15450
16151
  //#endregion
15451
16152
  //#region src/plugin/rules/a11y/img-redundant-alt.ts
15452
- const MESSAGE$49 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
16153
+ const MESSAGE$50 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
15453
16154
  const DEFAULT_COMPONENTS = ["img"];
15454
16155
  const DEFAULT_REDUNDANT_WORDS = [
15455
16156
  "image",
@@ -15514,7 +16215,7 @@ const imgRedundantAlt = defineRule({
15514
16215
  if (!altAttribute) return;
15515
16216
  if (altValueRedundant(altAttribute, settings.words)) context.report({
15516
16217
  node: altAttribute,
15517
- message: MESSAGE$49
16218
+ message: MESSAGE$50
15518
16219
  });
15519
16220
  } };
15520
16221
  }
@@ -16096,14 +16797,14 @@ const isBindingInvokedOnRenderPath = (root, bindingName) => {
16096
16797
  if (didFindRenderPathInvocation) return false;
16097
16798
  if (!isNodeOfType(node, "CallExpression")) return;
16098
16799
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== bindingName) return;
16099
- let enclosingFunction = findEnclosingFunction(node);
16800
+ let enclosingFunction = findEnclosingFunction$1(node);
16100
16801
  while (enclosingFunction) {
16101
16802
  if (isDeferredCallbackPosition$1(enclosingFunction) || getHandlerNamedBindingName(enclosingFunction)) return;
16102
16803
  if (containingFunctionIsComponentOrHook(enclosingFunction)) {
16103
16804
  didFindRenderPathInvocation = true;
16104
16805
  return;
16105
16806
  }
16106
- enclosingFunction = findEnclosingFunction(enclosingFunction);
16807
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
16107
16808
  }
16108
16809
  });
16109
16810
  return didFindRenderPathInvocation;
@@ -16138,7 +16839,7 @@ const jotaiSelectAtomInRenderBody = defineRule({
16138
16839
  };
16139
16840
  return { CallExpression(node) {
16140
16841
  if (!isImportedSelectAtom(node)) return;
16141
- const nearestFunctionLike = findEnclosingFunction(node);
16842
+ const nearestFunctionLike = findEnclosingFunction$1(node);
16142
16843
  if (!nearestFunctionLike) return;
16143
16844
  if (isDeferredCallback(nearestFunctionLike)) return;
16144
16845
  if (isBindingUsedAsHandler(nearestFunctionLike)) return;
@@ -17135,7 +17836,7 @@ const isPersistentCacheSetWrite = (callExpression, newExpression, enclosingFunct
17135
17836
  return !isAstDescendant(receiverBinding.scopeOwner, enclosingFunction);
17136
17837
  };
17137
17838
  const isInsideCacheMemo = (node) => {
17138
- const enclosingFunction = findEnclosingFunction(node);
17839
+ const enclosingFunction = findEnclosingFunction$1(node);
17139
17840
  let child = node;
17140
17841
  let cursor = node.parent ?? null;
17141
17842
  while (cursor) {
@@ -17173,7 +17874,7 @@ const getFunctionName = (functionNode) => {
17173
17874
  return null;
17174
17875
  };
17175
17876
  const isUncacheableOptionsMergeUtility = (node) => {
17176
- const enclosingFunction = findEnclosingFunction(node);
17877
+ const enclosingFunction = findEnclosingFunction$1(node);
17177
17878
  if (!enclosingFunction || !isFunctionLike$2(enclosingFunction)) return false;
17178
17879
  const functionName = getFunctionName(enclosingFunction);
17179
17880
  if (!functionName || isComponentOrHookName(functionName)) return false;
@@ -17297,7 +17998,7 @@ const GLOBAL_BUILTIN_NAMES = new Set([
17297
17998
  ]);
17298
17999
  const STRING_PROTOTYPE_PATH = "String.prototype";
17299
18000
  const REGEXP_PROTOTYPE_PATH = "RegExp.prototype";
17300
- const getStaticStringValue = (argument) => {
18001
+ const getStaticStringValue$1 = (argument) => {
17301
18002
  if (!argument) return null;
17302
18003
  const unwrappedArgument = stripParenExpression(argument);
17303
18004
  if (isNodeOfType(unwrappedArgument, "Literal") && typeof unwrappedArgument.value === "string") return unwrappedArgument.value;
@@ -17305,7 +18006,7 @@ const getStaticStringValue = (argument) => {
17305
18006
  return null;
17306
18007
  };
17307
18008
  const getEffectiveRegExpFlags = (patternArgument, flagsArgument) => {
17308
- if (flagsArgument) return getStaticStringValue(flagsArgument);
18009
+ if (flagsArgument) return getStaticStringValue$1(flagsArgument);
17309
18010
  if (!patternArgument) return "";
17310
18011
  const unwrappedPattern = stripParenExpression(patternArgument);
17311
18012
  if (isNodeOfType(unwrappedPattern, "Literal") && unwrappedPattern.value instanceof RegExp) return unwrappedPattern.value.flags;
@@ -17364,13 +18065,6 @@ const isSafeStatefulReplaceAllSearch = (node, flags, context) => {
17364
18065
  const callee = stripParenExpression(replaceAllCall.callee);
17365
18066
  return isNodeOfType(callee, "MemberExpression") && !callee.computed && !callee.optional && !replaceAllCall.optional && isNodeOfType(callee.property, "Identifier") && callee.property.name === "replaceAll" && isProvenNativeStringReceiver(callee.object, context);
17366
18067
  };
17367
- const getDestructuredBindingPropertyName = (bindingIdentifier) => {
17368
- let bindingNode = bindingIdentifier;
17369
- if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
17370
- const property = bindingNode.parent;
17371
- if (!isNodeOfType(property, "Property") || property.value !== bindingNode || !isNodeOfType(property.parent, "ObjectPattern")) return null;
17372
- return getStaticPropertyKeyName(property, { allowComputedString: true });
17373
- };
17374
18068
  const extendGlobalPath = (basePath, propertyName) => {
17375
18069
  if (basePath === "global") {
17376
18070
  if (propertyName === null || GLOBAL_OBJECT_NAMES.has(propertyName)) return "global";
@@ -17455,7 +18149,7 @@ const getCallRegExpHazard = (node, context, symbolCache) => {
17455
18149
  const mutationHazard = targetPath === "global" ? "globalRegExpReplaced" : "replaceAllIntegrityLost";
17456
18150
  const guardedPropertyName = targetPath === "global" ? "RegExp" : "replaceAll";
17457
18151
  if (isSinglePropertyMutation) {
17458
- const propertyName = getStaticStringValue(node.arguments?.[1]);
18152
+ const propertyName = getStaticStringValue$1(node.arguments?.[1]);
17459
18153
  return propertyName === null || propertyName === guardedPropertyName ? mutationHazard : "none";
17460
18154
  }
17461
18155
  if (!isPropertyCollectionMutation) return "none";
@@ -17484,7 +18178,7 @@ const getHoistableRegExpConstructionKind = (node, context) => {
17484
18178
  if (!STATEFUL_REGEXP_FLAGS_PATTERN.test(effectiveFlags)) return "stateless";
17485
18179
  return isSafeStatefulReplaceAllSearch(node, effectiveFlags, context) ? "statefulReplaceAll" : null;
17486
18180
  };
17487
- const MESSAGE$48 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
18181
+ const MESSAGE$49 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
17488
18182
  const jsHoistRegexp = defineRule({
17489
18183
  id: "js-hoist-regexp",
17490
18184
  title: "RegExp built inside a loop",
@@ -17501,7 +18195,7 @@ const jsHoistRegexp = defineRule({
17501
18195
  if (constructionKind === "statefulReplaceAll" && cachedEnvironmentHazard === "replaceAllIntegrityLost") return;
17502
18196
  context.report({
17503
18197
  node,
17504
- message: MESSAGE$48
18198
+ message: MESSAGE$49
17505
18199
  });
17506
18200
  };
17507
18201
  return createLoopAwareVisitors({
@@ -20121,7 +20815,7 @@ const jsxMaxDepth = defineRule({
20121
20815
  });
20122
20816
  //#endregion
20123
20817
  //#region src/plugin/rules/react-builtins/jsx-no-comment-textnodes.ts
20124
- const MESSAGE$47 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20818
+ const MESSAGE$48 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20125
20819
  const LITERAL_TEXT_TAGS = new Set([
20126
20820
  "code",
20127
20821
  "pre",
@@ -20191,7 +20885,7 @@ const jsxNoCommentTextnodes = defineRule({
20191
20885
  if (isDeliberateStyledCommentToken(node)) return;
20192
20886
  context.report({
20193
20887
  node,
20194
- message: MESSAGE$47
20888
+ message: MESSAGE$48
20195
20889
  });
20196
20890
  } })
20197
20891
  });
@@ -20222,7 +20916,7 @@ const isInsideFunctionScope = (node) => {
20222
20916
  };
20223
20917
  //#endregion
20224
20918
  //#region src/plugin/rules/react-builtins/jsx-no-constructed-context-values.ts
20225
- const MESSAGE$46 = "Every reader of this context redraws on each render because you build its `value` inline.";
20919
+ const MESSAGE$47 = "Every reader of this context redraws on each render because you build its `value` inline.";
20226
20920
  const CONTEXT_MODULES$1 = [
20227
20921
  "react",
20228
20922
  "use-context-selector",
@@ -20320,7 +21014,7 @@ const jsxNoConstructedContextValues = defineRule({
20320
21014
  if (!isConstructedValue(innerExpression)) continue;
20321
21015
  context.report({
20322
21016
  node: attribute,
20323
- message: MESSAGE$46
21017
+ message: MESSAGE$47
20324
21018
  });
20325
21019
  }
20326
21020
  }
@@ -21175,7 +21869,7 @@ const DATA_ARRAY_PROP_SUFFIXES = [
21175
21869
  ];
21176
21870
  //#endregion
21177
21871
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop.ts
21178
- const MESSAGE$45 = "This child redraws every render because the prop gets a brand new array each time.";
21872
+ const MESSAGE$46 = "This child redraws every render because the prop gets a brand new array each time.";
21179
21873
  const isDataArrayPropName = (propName) => {
21180
21874
  if (DATA_ARRAY_PROP_NAMES.has(propName)) return true;
21181
21875
  for (const suffix of DATA_ARRAY_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -21262,7 +21956,7 @@ const jsxNoNewArrayAsProp = defineRule({
21262
21956
  if (!isArrayProducingExpression(expressionNode) && !followsRenderLocalArrayBinding(expressionNode, node)) return;
21263
21957
  context.report({
21264
21958
  node,
21265
- message: MESSAGE$45
21959
+ message: MESSAGE$46
21266
21960
  });
21267
21961
  }
21268
21962
  };
@@ -21520,7 +22214,7 @@ const SAFE_RECEIVER_NAMES = new Set([
21520
22214
  ]);
21521
22215
  //#endregion
21522
22216
  //#region src/plugin/rules/react-builtins/jsx-no-new-function-as-prop.ts
21523
- const MESSAGE$44 = "This child redraws every render because the prop gets a brand new function each time.";
22217
+ const MESSAGE$45 = "This child redraws every render because the prop gets a brand new function each time.";
21524
22218
  const isAccessorPredicateName = (propName) => {
21525
22219
  for (const prefix of ACCESSOR_PREDICATE_PREFIXES) {
21526
22220
  if (propName.length <= prefix.length) continue;
@@ -21726,7 +22420,7 @@ const jsxNoNewFunctionAsProp = defineRule({
21726
22420
  if (!isFunctionProducingExpression(expressionNode) && !followsRenderLocalFunctionBinding(expressionNode, node)) return;
21727
22421
  context.report({
21728
22422
  node,
21729
- message: MESSAGE$44
22423
+ message: MESSAGE$45
21730
22424
  });
21731
22425
  }
21732
22426
  };
@@ -21946,7 +22640,7 @@ const CONFIG_OBJECT_PROP_SUFFIXES = [
21946
22640
  ];
21947
22641
  //#endregion
21948
22642
  //#region src/plugin/rules/react-builtins/jsx-no-new-object-as-prop.ts
21949
- const MESSAGE$43 = "This child redraws every render because the prop gets a brand new object each time.";
22643
+ const MESSAGE$44 = "This child redraws every render because the prop gets a brand new object each time.";
21950
22644
  const isConfigObjectPropName = (propName) => {
21951
22645
  if (CONFIG_OBJECT_PROP_NAMES.has(propName)) return true;
21952
22646
  for (const suffix of CONFIG_OBJECT_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -22035,7 +22729,7 @@ const jsxNoNewObjectAsProp = defineRule({
22035
22729
  if (!isObjectProducingExpression(expressionNode) && !followsRenderLocalObjectBinding(expressionNode, node)) return;
22036
22730
  context.report({
22037
22731
  node,
22038
- message: MESSAGE$43
22732
+ message: MESSAGE$44
22039
22733
  });
22040
22734
  }
22041
22735
  };
@@ -22043,7 +22737,7 @@ const jsxNoNewObjectAsProp = defineRule({
22043
22737
  });
22044
22738
  //#endregion
22045
22739
  //#region src/plugin/rules/react-builtins/jsx-no-script-url.ts
22046
- const MESSAGE$42 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
22740
+ const MESSAGE$43 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
22047
22741
  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;
22048
22742
  const resolveSettings$29 = (settings) => {
22049
22743
  const reactDoctor = settings?.["react-doctor"];
@@ -22081,7 +22775,7 @@ const jsxNoScriptUrl = defineRule({
22081
22775
  if (!value || !isNodeOfType(value, "Literal") || typeof value.value !== "string") continue;
22082
22776
  if (JAVASCRIPT_URL_PATTERN.test(value.value)) context.report({
22083
22777
  node: attribute,
22084
- message: MESSAGE$42
22778
+ message: MESSAGE$43
22085
22779
  });
22086
22780
  }
22087
22781
  } };
@@ -22701,7 +23395,7 @@ const jsxPropsNoSpreadMulti = defineRule({
22701
23395
  });
22702
23396
  //#endregion
22703
23397
  //#region src/plugin/rules/react-builtins/jsx-props-no-spreading.ts
22704
- const MESSAGE$41 = "You can't tell what props reach this element when you spread them.";
23398
+ const MESSAGE$42 = "You can't tell what props reach this element when you spread them.";
22705
23399
  const resolveSettings$25 = (settings) => {
22706
23400
  const reactDoctor = settings?.["react-doctor"];
22707
23401
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxPropsNoSpreading ?? {} : {};
@@ -22744,7 +23438,7 @@ const jsxPropsNoSpreading = defineRule({
22744
23438
  }
22745
23439
  context.report({
22746
23440
  node: attribute,
22747
- message: MESSAGE$41
23441
+ message: MESSAGE$42
22748
23442
  });
22749
23443
  didReportInFile = true;
22750
23444
  return;
@@ -23020,7 +23714,7 @@ const labelHasAssociatedControl = defineRule({
23020
23714
  });
23021
23715
  //#endregion
23022
23716
  //#region src/plugin/rules/a11y/lang.ts
23023
- 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`.";
23717
+ 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`.";
23024
23718
  const COMMON_LANGUAGE_PRIMARY_TAGS = new Set([
23025
23719
  "aa",
23026
23720
  "ab",
@@ -23500,7 +24194,7 @@ const lang = defineRule({
23500
24194
  if (expression.type === "Identifier" && expression.name === "undefined" || expression.type === "Literal" && expression.value === null) {
23501
24195
  context.report({
23502
24196
  node: langAttr,
23503
- message: MESSAGE$40
24197
+ message: MESSAGE$41
23504
24198
  });
23505
24199
  return;
23506
24200
  }
@@ -23509,7 +24203,7 @@ const lang = defineRule({
23509
24203
  if (value === null) return;
23510
24204
  if (!isValidLangTag(value)) context.report({
23511
24205
  node: langAttr,
23512
- message: MESSAGE$40
24206
+ message: MESSAGE$41
23513
24207
  });
23514
24208
  } })
23515
24209
  });
@@ -23560,7 +24254,7 @@ const mdxSsrExecutionRisk = defineRule({
23560
24254
  });
23561
24255
  //#endregion
23562
24256
  //#region src/plugin/rules/a11y/media-has-caption.ts
23563
- const MESSAGE$39 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
24257
+ const MESSAGE$40 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
23564
24258
  const DEFAULT_AUDIO = ["audio"];
23565
24259
  const DEFAULT_VIDEO = ["video"];
23566
24260
  const DEFAULT_TRACK = ["track"];
@@ -23649,7 +24343,7 @@ const mediaHasCaption = defineRule({
23649
24343
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
23650
24344
  context.report({
23651
24345
  node: node.name,
23652
- message: MESSAGE$39
24346
+ message: MESSAGE$40
23653
24347
  });
23654
24348
  return;
23655
24349
  }
@@ -23668,7 +24362,7 @@ const mediaHasCaption = defineRule({
23668
24362
  return kindValue.value.toLowerCase() === "captions";
23669
24363
  })) context.report({
23670
24364
  node: node.name,
23671
- message: MESSAGE$39
24365
+ message: MESSAGE$40
23672
24366
  });
23673
24367
  } };
23674
24368
  }
@@ -25211,7 +25905,7 @@ const nextjsNoVercelOgImport = defineRule({
25211
25905
  });
25212
25906
  //#endregion
25213
25907
  //#region src/plugin/rules/a11y/no-access-key.ts
25214
- const MESSAGE$38 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25908
+ const MESSAGE$39 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25215
25909
  const isUndefinedIdentifier = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
25216
25910
  const noAccessKey = defineRule({
25217
25911
  id: "no-access-key",
@@ -25230,7 +25924,7 @@ const noAccessKey = defineRule({
25230
25924
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
25231
25925
  context.report({
25232
25926
  node: accessKey,
25233
- message: MESSAGE$38
25927
+ message: MESSAGE$39
25234
25928
  });
25235
25929
  return;
25236
25930
  }
@@ -25240,7 +25934,7 @@ const noAccessKey = defineRule({
25240
25934
  if (isUndefinedIdentifier(expression)) return;
25241
25935
  context.report({
25242
25936
  node: accessKey,
25243
- message: MESSAGE$38
25937
+ message: MESSAGE$39
25244
25938
  });
25245
25939
  }
25246
25940
  } };
@@ -26117,7 +26811,7 @@ const isIndependentWriterIdentifier = (componentFunction, identifier, includeDef
26117
26811
  };
26118
26812
  const hasUserInputSetterWriter = (setterRef, effectNode, includeDeferredWriters = false) => {
26119
26813
  if (!setterRef.resolved) return false;
26120
- const componentFunction = findEnclosingFunction(effectNode);
26814
+ const componentFunction = findEnclosingFunction$1(effectNode);
26121
26815
  if (!componentFunction) return false;
26122
26816
  for (const reference of setterRef.resolved.references) {
26123
26817
  if (reference.init) continue;
@@ -27095,7 +27789,7 @@ const noAdjustStateOnPropChange = defineRule({
27095
27789
  });
27096
27790
  //#endregion
27097
27791
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
27098
- 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.";
27792
+ 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.";
27099
27793
  const ALWAYS_FOCUSABLE_TAGS = new Set([
27100
27794
  "button",
27101
27795
  "embed",
@@ -27207,7 +27901,7 @@ const noAriaHiddenOnFocusable = defineRule({
27207
27901
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
27208
27902
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
27209
27903
  node: ariaHidden,
27210
- message: MESSAGE$37
27904
+ message: MESSAGE$38
27211
27905
  });
27212
27906
  } })
27213
27907
  });
@@ -28129,7 +28823,7 @@ const isAllLiteralArrayExpression = (node) => {
28129
28823
  };
28130
28824
  //#endregion
28131
28825
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
28132
- const MESSAGE$36 = "Your users can see & submit the wrong data when this list reorders.";
28826
+ const MESSAGE$37 = "Your users can see & submit the wrong data when this list reorders.";
28133
28827
  const SECOND_INDEX_METHODS = new Set([
28134
28828
  "every",
28135
28829
  "filter",
@@ -28248,14 +28942,14 @@ const noArrayIndexKey = defineRule({
28248
28942
  if (propName !== "key") continue;
28249
28943
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
28250
28944
  node: property,
28251
- message: MESSAGE$36
28945
+ message: MESSAGE$37
28252
28946
  });
28253
28947
  }
28254
28948
  } })
28255
28949
  });
28256
28950
  //#endregion
28257
28951
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
28258
- 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.";
28952
+ 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.";
28259
28953
  const noAsyncEffectCallback = defineRule({
28260
28954
  id: "no-async-effect-callback",
28261
28955
  title: "Async effect callback",
@@ -28273,13 +28967,13 @@ const noAsyncEffectCallback = defineRule({
28273
28967
  if (!callback.async) return;
28274
28968
  context.report({
28275
28969
  node: callback,
28276
- message: MESSAGE$35
28970
+ message: MESSAGE$36
28277
28971
  });
28278
28972
  } })
28279
28973
  });
28280
28974
  //#endregion
28281
28975
  //#region src/plugin/rules/a11y/no-autofocus.ts
28282
- 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.";
28976
+ 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.";
28283
28977
  const resolveSettings$21 = (settings) => {
28284
28978
  const reactDoctor = settings?.["react-doctor"];
28285
28979
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -28376,7 +29070,7 @@ const noAutofocus = defineRule({
28376
29070
  if (isConditionallyRendered(node)) return;
28377
29071
  context.report({
28378
29072
  node: autoFocusAttribute,
28379
- message: MESSAGE$34
29073
+ message: MESSAGE$35
28380
29074
  });
28381
29075
  } };
28382
29076
  }
@@ -28890,138 +29584,6 @@ const noBarrelImport = defineRule({
28890
29584
  }
28891
29585
  });
28892
29586
  //#endregion
28893
- //#region src/plugin/utils/has-static-property-write-before.ts
28894
- const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
28895
- const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
28896
- "CatchClause",
28897
- "ConditionalExpression",
28898
- "DoWhileStatement",
28899
- "ForInStatement",
28900
- "ForOfStatement",
28901
- "ForStatement",
28902
- "IfStatement",
28903
- "LogicalExpression",
28904
- "SwitchCase",
28905
- "SwitchStatement",
28906
- "TryStatement",
28907
- "WhileStatement"
28908
- ]);
28909
- const getResolvedStaticPropertyName = (memberExpression, scopes) => {
28910
- if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
28911
- const directPropertyName = getStaticPropertyName(memberExpression);
28912
- if (directPropertyName || !memberExpression.computed) return directPropertyName;
28913
- const property = stripParenExpression(memberExpression.property);
28914
- if (!isNodeOfType(property, "Identifier")) return null;
28915
- const propertySymbol = resolveConstIdentifierAlias(property, scopes);
28916
- const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
28917
- return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
28918
- };
28919
- const collectScopeSymbols = (scope, symbols) => {
28920
- symbols.push(...scope.symbols);
28921
- for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
28922
- };
28923
- const getEquivalentSymbols = (identifier, scopes) => {
28924
- const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
28925
- if (!rootSymbol) return [];
28926
- let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
28927
- if (!symbolsByRootId) {
28928
- symbolsByRootId = /* @__PURE__ */ new Map();
28929
- equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
28930
- }
28931
- const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
28932
- if (cachedSymbols) return cachedSymbols;
28933
- const allSymbols = [];
28934
- collectScopeSymbols(scopes.rootScope, allSymbols);
28935
- const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
28936
- symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
28937
- return equivalentSymbols;
28938
- };
28939
- const findExecutionBoundary = (node) => {
28940
- let current = node;
28941
- while (current) {
28942
- if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
28943
- current = current.parent ?? null;
28944
- }
28945
- return null;
28946
- };
28947
- const isOnUnconditionalPath = (node, boundary) => {
28948
- let current = node.parent ?? null;
28949
- while (current && current !== boundary) {
28950
- if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
28951
- current = current.parent ?? null;
28952
- }
28953
- return current === boundary;
28954
- };
28955
- const findFunctionBindingIdentifier = (functionNode) => {
28956
- if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
28957
- const parent = functionNode.parent;
28958
- if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
28959
- return null;
28960
- };
28961
- const findDirectCall = (identifier) => {
28962
- let callee = identifier;
28963
- let parent = callee.parent;
28964
- while (parent && stripParenExpression(parent) === identifier) {
28965
- callee = parent;
28966
- parent = callee.parent;
28967
- }
28968
- return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
28969
- };
28970
- const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
28971
- if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
28972
- visitedFunctionNodes.add(functionNode);
28973
- const referenceBoundary = findExecutionBoundary(referenceNode);
28974
- if (!referenceBoundary) return false;
28975
- const invocationCalls = [];
28976
- const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
28977
- if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
28978
- const call = findDirectCall(reference.identifier);
28979
- if (call) invocationCalls.push(call);
28980
- }
28981
- else {
28982
- const call = findDirectCall(functionNode);
28983
- if (call) invocationCalls.push(call);
28984
- }
28985
- return invocationCalls.some((call) => {
28986
- if (call.range[0] >= referenceNode.range[0]) return false;
28987
- const callBoundary = findExecutionBoundary(call);
28988
- if (!callBoundary) return false;
28989
- if (callBoundary === referenceBoundary) return true;
28990
- if (!isFunctionLike$2(callBoundary)) return false;
28991
- return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
28992
- });
28993
- };
28994
- const isMemberWriteTarget = (memberExpression) => {
28995
- const parent = memberExpression.parent;
28996
- if (!parent) return false;
28997
- if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
28998
- if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
28999
- return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
29000
- };
29001
- const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
29002
- let parent = reference.identifier.parent;
29003
- while (parent && stripParenExpression(parent) === reference.identifier) parent = parent.parent;
29004
- if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== reference.identifier || getResolvedStaticPropertyName(parent, scopes) !== propertyName || !isMemberWriteTarget(parent)) return false;
29005
- const writeBoundary = findExecutionBoundary(parent);
29006
- const referenceBoundary = findExecutionBoundary(referenceNode);
29007
- if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(parent, writeBoundary)) return false;
29008
- if (writeBoundary === referenceBoundary) return parent.range[0] < referenceNode.range[0];
29009
- return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
29010
- });
29011
- const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
29012
- if (!isNodeOfType(identifier, "Identifier")) return false;
29013
- return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
29014
- };
29015
- //#endregion
29016
- //#region src/plugin/utils/has-symbol-write-before.ts
29017
- const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
29018
- if (reference.flag === "read") return false;
29019
- const writeFunction = findEnclosingFunction(reference.identifier);
29020
- if (writeFunction === findEnclosingFunction(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
29021
- if (!writeFunction) return true;
29022
- return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
29023
- });
29024
- //#endregion
29025
29587
  //#region src/plugin/utils/has-stable-call-target.ts
29026
29588
  const hasStableCallTarget = (callExpression, scopes) => {
29027
29589
  if (!isNodeOfType(callExpression, "CallExpression")) return false;
@@ -29349,7 +29911,7 @@ const noChainStateUpdates = defineRule({
29349
29911
  });
29350
29912
  //#endregion
29351
29913
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
29352
- const MESSAGE$33 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29914
+ const MESSAGE$34 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29353
29915
  const noChildrenProp = defineRule({
29354
29916
  id: "no-children-prop",
29355
29917
  title: "Children passed as a prop",
@@ -29361,7 +29923,7 @@ const noChildrenProp = defineRule({
29361
29923
  if (node.name.name !== "children") return;
29362
29924
  context.report({
29363
29925
  node: node.name,
29364
- message: MESSAGE$33
29926
+ message: MESSAGE$34
29365
29927
  });
29366
29928
  },
29367
29929
  CallExpression(node) {
@@ -29374,7 +29936,7 @@ const noChildrenProp = defineRule({
29374
29936
  const propertyKey = property.key;
29375
29937
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
29376
29938
  node: propertyKey,
29377
- message: MESSAGE$33
29939
+ message: MESSAGE$34
29378
29940
  });
29379
29941
  }
29380
29942
  }
@@ -29382,7 +29944,7 @@ const noChildrenProp = defineRule({
29382
29944
  });
29383
29945
  //#endregion
29384
29946
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
29385
- const MESSAGE$32 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29947
+ const MESSAGE$33 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29386
29948
  const noCloneElement = defineRule({
29387
29949
  id: "no-clone-element",
29388
29950
  title: "cloneElement makes child props fragile",
@@ -29395,7 +29957,7 @@ const noCloneElement = defineRule({
29395
29957
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
29396
29958
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
29397
29959
  node: callee,
29398
- message: MESSAGE$32
29960
+ message: MESSAGE$33
29399
29961
  });
29400
29962
  return;
29401
29963
  }
@@ -29408,7 +29970,7 @@ const noCloneElement = defineRule({
29408
29970
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
29409
29971
  context.report({
29410
29972
  node: callee,
29411
- message: MESSAGE$32
29973
+ message: MESSAGE$33
29412
29974
  });
29413
29975
  }
29414
29976
  } })
@@ -29428,7 +29990,7 @@ const getCallMethodName = (callee) => {
29428
29990
  };
29429
29991
  //#endregion
29430
29992
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
29431
- const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29993
+ const MESSAGE$32 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29432
29994
  const CONTEXT_MODULES = [
29433
29995
  "react",
29434
29996
  "use-context-selector",
@@ -29476,13 +30038,13 @@ const noCreateContextInRender = defineRule({
29476
30038
  if (!componentOrHookName) return;
29477
30039
  context.report({
29478
30040
  node,
29479
- message: `${MESSAGE$31} (called inside "${componentOrHookName}")`
30041
+ message: `${MESSAGE$32} (called inside "${componentOrHookName}")`
29480
30042
  });
29481
30043
  } })
29482
30044
  });
29483
30045
  //#endregion
29484
30046
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
29485
- 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.";
30047
+ 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.";
29486
30048
  const isUseMemoCallbackArgument = (functionNode) => {
29487
30049
  const parent = functionNode.parent;
29488
30050
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -29490,8 +30052,8 @@ const isUseMemoCallbackArgument = (functionNode) => {
29490
30052
  return isReactFunctionCall(parent, "useMemo");
29491
30053
  };
29492
30054
  const findEnclosingRenderFunction = (node) => {
29493
- let enclosingFunction = findEnclosingFunction(node);
29494
- while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
30055
+ let enclosingFunction = findEnclosingFunction$1(node);
30056
+ while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
29495
30057
  return enclosingFunction;
29496
30058
  };
29497
30059
  const noCreateRefInFunctionComponent = defineRule({
@@ -29512,7 +30074,7 @@ const noCreateRefInFunctionComponent = defineRule({
29512
30074
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes, context.cfg))) return;
29513
30075
  context.report({
29514
30076
  node,
29515
- message: MESSAGE$30
30077
+ message: MESSAGE$31
29516
30078
  });
29517
30079
  } })
29518
30080
  });
@@ -29652,7 +30214,7 @@ const noCreateStoreInRender = defineRule({
29652
30214
  });
29653
30215
  //#endregion
29654
30216
  //#region src/plugin/rules/react-builtins/no-danger.ts
29655
- const MESSAGE$29 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
30217
+ const MESSAGE$30 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
29656
30218
  const noDanger = defineRule({
29657
30219
  id: "no-danger",
29658
30220
  title: "Raw HTML injection can run unsafe markup",
@@ -29666,7 +30228,7 @@ const noDanger = defineRule({
29666
30228
  if (!propAttribute) return;
29667
30229
  context.report({
29668
30230
  node: propAttribute.name,
29669
- message: MESSAGE$29
30231
+ message: MESSAGE$30
29670
30232
  });
29671
30233
  },
29672
30234
  CallExpression(node) {
@@ -29678,7 +30240,7 @@ const noDanger = defineRule({
29678
30240
  const propertyKey = property.key;
29679
30241
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
29680
30242
  node: propertyKey,
29681
- message: MESSAGE$29
30243
+ message: MESSAGE$30
29682
30244
  });
29683
30245
  }
29684
30246
  }
@@ -29701,7 +30263,7 @@ const isMeaningfulJsxChild = (child) => {
29701
30263
  };
29702
30264
  //#endregion
29703
30265
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
29704
- const MESSAGE$28 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
30266
+ const MESSAGE$29 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
29705
30267
  const mergePropsShape = (target, source) => {
29706
30268
  target.hasDangerously ||= source.hasDangerously;
29707
30269
  target.hasChildren ||= source.hasChildren;
@@ -29776,7 +30338,7 @@ const noDangerWithChildren = defineRule({
29776
30338
  if (!hasChildrenProp && !hasNestedChildren) return;
29777
30339
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
29778
30340
  node: opening,
29779
- message: MESSAGE$28
30341
+ message: MESSAGE$29
29780
30342
  });
29781
30343
  },
29782
30344
  CallExpression(node) {
@@ -29789,7 +30351,7 @@ const noDangerWithChildren = defineRule({
29789
30351
  const positionalChildren = node.arguments.slice(2);
29790
30352
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
29791
30353
  node,
29792
- message: MESSAGE$28
30354
+ message: MESSAGE$29
29793
30355
  });
29794
30356
  }
29795
30357
  })
@@ -30542,7 +31104,7 @@ const getEnclosingEffectHookCallback = (node, componentFunction) => {
30542
31104
  const isEffectDrivenResync = (useStateCall) => {
30543
31105
  const setterName = getStateSetterName(useStateCall);
30544
31106
  if (!setterName) return false;
30545
- const componentFunction = findEnclosingFunction(useStateCall);
31107
+ const componentFunction = findEnclosingFunction$1(useStateCall);
30546
31108
  if (!componentFunction) return false;
30547
31109
  let isExempt = false;
30548
31110
  walkAst(componentFunction, (child) => {
@@ -30646,7 +31208,7 @@ const isDraftReseedOrRenderAdjusted = (useStateCall, isPropName) => {
30646
31208
  const setterName = getStateSetterName(useStateCall);
30647
31209
  if (!setterName) return false;
30648
31210
  const stateValueName = getStateValueName(useStateCall);
30649
- const componentFunction = findEnclosingFunction(useStateCall);
31211
+ const componentFunction = findEnclosingFunction$1(useStateCall);
30650
31212
  if (!componentFunction) return false;
30651
31213
  let isExempt = false;
30652
31214
  walkAst(componentFunction, (child) => {
@@ -30692,7 +31254,7 @@ const noDerivedUseState = defineRule({
30692
31254
  if (isDraftReseedOrRenderAdjusted(node, propStackTracker.isPropName)) return;
30693
31255
  if (isEffectDrivenResync(node)) return;
30694
31256
  if (isNextjsDataFetchingPage(node)) return;
30695
- const componentFunction = findEnclosingFunction(node);
31257
+ const componentFunction = findEnclosingFunction$1(node);
30696
31258
  if (componentFunction) {
30697
31259
  if (isDefaultedDestructuredProp(componentFunction, propName)) return;
30698
31260
  if (isDraftCommittedToParent(componentFunction, getStateValueName(node), propStackTracker.isPropName)) return;
@@ -30754,7 +31316,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
30754
31316
  //#endregion
30755
31317
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
30756
31318
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
30757
- const MESSAGE$27 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
31319
+ const MESSAGE$28 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
30758
31320
  const getNodeStart = (node) => "start" in node && typeof node.start === "number" ? node.start : -1;
30759
31321
  const getEnclosingLifecycleFunction = (setStateCall) => {
30760
31322
  let ancestor = setStateCall.parent;
@@ -30882,7 +31444,7 @@ const noDidMountSetState = defineRule({
30882
31444
  }
30883
31445
  context.report({
30884
31446
  node: node.callee,
30885
- message: MESSAGE$27
31447
+ message: MESSAGE$28
30886
31448
  });
30887
31449
  } };
30888
31450
  }
@@ -30890,7 +31452,7 @@ const noDidMountSetState = defineRule({
30890
31452
  //#endregion
30891
31453
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
30892
31454
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
30893
- const MESSAGE$26 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
31455
+ const MESSAGE$27 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
30894
31456
  const EQUALITY_OPERATORS = new Set([
30895
31457
  "==",
30896
31458
  "===",
@@ -31064,7 +31626,7 @@ const noDidUpdateSetState = defineRule({
31064
31626
  if (isInsideDiffGuard(node)) return;
31065
31627
  context.report({
31066
31628
  node: node.callee,
31067
- message: MESSAGE$26
31629
+ message: MESSAGE$27
31068
31630
  });
31069
31631
  } };
31070
31632
  }
@@ -31087,7 +31649,7 @@ const isStateMemberExpression = (node) => {
31087
31649
  };
31088
31650
  //#endregion
31089
31651
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
31090
- 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.";
31652
+ 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.";
31091
31653
  const shouldIgnoreMutation = (node) => {
31092
31654
  let isConstructor = false;
31093
31655
  let isInsideCallExpression = false;
@@ -31109,7 +31671,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
31109
31671
  if (shouldIgnoreMutation(reportNode)) return;
31110
31672
  context.report({
31111
31673
  node: reportNode,
31112
- message: MESSAGE$25
31674
+ message: MESSAGE$26
31113
31675
  });
31114
31676
  };
31115
31677
  const noDirectMutationState = defineRule({
@@ -31460,7 +32022,7 @@ const noDocumentStartViewTransition = defineRule({
31460
32022
  });
31461
32023
  //#endregion
31462
32024
  //#region src/plugin/rules/js-performance/no-document-write.ts
31463
- 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.";
32025
+ 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.";
31464
32026
  const WRITE_METHODS = new Set(["write", "writeln"]);
31465
32027
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
31466
32028
  const noDocumentWrite = defineRule({
@@ -31477,7 +32039,7 @@ const noDocumentWrite = defineRule({
31477
32039
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
31478
32040
  context.report({
31479
32041
  node,
31480
- message: MESSAGE$24
32042
+ message: MESSAGE$25
31481
32043
  });
31482
32044
  } })
31483
32045
  });
@@ -32530,7 +33092,7 @@ const isFunctionUsedOutsideHandlers = (analysis, functionNode, componentFunction
32530
33092
  if (isInsideInlineEventHandler(identifier, componentFunction)) return false;
32531
33093
  const parent = identifier.parent;
32532
33094
  if (parent && isNodeOfType(parent, "CallExpression") && parent.callee === identifier) {
32533
- if (findEnclosingFunction(parent) === functionNode) return false;
33095
+ if (findEnclosingFunction$1(parent) === functionNode) return false;
32534
33096
  if (isInsideProvenEventHandler(analysis, parent, componentFunction, false)) return false;
32535
33097
  }
32536
33098
  return true;
@@ -32576,7 +33138,7 @@ const isStateWrittenOnlyFromEventHandlers = (analysis, stateReference) => {
32576
33138
  if (!setterVariable) return false;
32577
33139
  const stateDeclarator = getUseStateDecl(analysis, stateReference);
32578
33140
  if (!stateDeclarator) return false;
32579
- const componentFunction = findEnclosingFunction(stateDeclarator);
33141
+ const componentFunction = findEnclosingFunction$1(stateDeclarator);
32580
33142
  if (!componentFunction) return false;
32581
33143
  let hasWriter = false;
32582
33144
  for (const reference of setterVariable.references) {
@@ -33638,8 +34200,8 @@ const isAwaitedInFunction = (request, functionNode) => {
33638
34200
  return false;
33639
34201
  };
33640
34202
  const isCompletionSinkForRequest = (completionSink, request) => {
33641
- const requestFunction = findEnclosingFunction(request);
33642
- const completionSinkFunction = findEnclosingFunction(completionSink);
34203
+ const requestFunction = findEnclosingFunction$1(request);
34204
+ const completionSinkFunction = findEnclosingFunction$1(completionSink);
33643
34205
  if (!requestFunction || !completionSinkFunction) return false;
33644
34206
  if (requestFunction === completionSinkFunction) {
33645
34207
  if (!isAwaitedInFunction(request, requestFunction)) return false;
@@ -33695,7 +34257,7 @@ const ALLOWED_NAMESPACES = new Set([
33695
34257
  "ReactDOM",
33696
34258
  "ReactDom"
33697
34259
  ]);
33698
- const MESSAGE$23 = "`findDOMNode` crashes your app in React 19 because it was removed.";
34260
+ const MESSAGE$24 = "`findDOMNode` crashes your app in React 19 because it was removed.";
33699
34261
  const noFindDomNode = defineRule({
33700
34262
  id: "no-find-dom-node",
33701
34263
  title: "findDOMNode breaks component encapsulation",
@@ -33706,7 +34268,7 @@ const noFindDomNode = defineRule({
33706
34268
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
33707
34269
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
33708
34270
  node: callee,
33709
- message: MESSAGE$23
34271
+ message: MESSAGE$24
33710
34272
  });
33711
34273
  return;
33712
34274
  }
@@ -33717,7 +34279,7 @@ const noFindDomNode = defineRule({
33717
34279
  if (callee.property.name !== "findDOMNode") return;
33718
34280
  context.report({
33719
34281
  node: callee.property,
33720
- message: MESSAGE$23
34282
+ message: MESSAGE$24
33721
34283
  });
33722
34284
  }
33723
34285
  } })
@@ -33926,13 +34488,6 @@ const isPublishedLibraryPackage = (filename) => {
33926
34488
  return manifest.private !== true && typeof manifest.peerDependencies === "object" && manifest.peerDependencies !== null && "react" in manifest.peerDependencies;
33927
34489
  };
33928
34490
  //#endregion
33929
- //#region src/plugin/utils/is-type-only-import.ts
33930
- const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
33931
- if (!specifiers || specifiers.length === 0) return false;
33932
- return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
33933
- };
33934
- const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
33935
- //#endregion
33936
34491
  //#region src/plugin/rules/bundle-size/no-full-lodash-import.ts
33937
34492
  const isLibraryDevPage = (filename) => {
33938
34493
  if (!filename) return false;
@@ -34496,7 +35051,7 @@ const isInRenderedOutput = (node, componentOrHookNode, scopes) => {
34496
35051
  return attribute ? !isEventHandlerAttribute(attribute) : true;
34497
35052
  }
34498
35053
  if (isNodeOfType(parentNode, "ReturnStatement")) {
34499
- if (findEnclosingFunction(parentNode) === componentOrHookNode) return true;
35054
+ if (findEnclosingFunction$1(parentNode) === componentOrHookNode) return true;
34500
35055
  }
34501
35056
  if (parentNode === componentOrHookNode) return isFunctionLike$2(componentOrHookNode) && !isNodeOfType(componentOrHookNode.body, "BlockStatement") && componentOrHookNode.body === currentNode;
34502
35057
  if (isFunctionLike$2(parentNode) && !executesDuringRender(parentNode, scopes)) return false;
@@ -34619,7 +35174,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
34619
35174
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
34620
35175
  const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
34621
35176
  if (!componentOrHookNode) return;
34622
- const enclosingFunction = findEnclosingFunction(node);
35177
+ const enclosingFunction = findEnclosingFunction$1(node);
34623
35178
  if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
34624
35179
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
34625
35180
  if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
@@ -34631,7 +35186,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
34631
35186
  });
34632
35187
  //#endregion
34633
35188
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
34634
- 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.";
35189
+ 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.";
34635
35190
  const noImgLazyWithHighFetchpriority = defineRule({
34636
35191
  id: "no-img-lazy-with-high-fetchpriority",
34637
35192
  title: "Lazy image with high fetchPriority",
@@ -34645,7 +35200,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
34645
35200
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
34646
35201
  context.report({
34647
35202
  node: node.name,
34648
- message: MESSAGE$22
35203
+ message: MESSAGE$23
34649
35204
  });
34650
35205
  } })
34651
35206
  });
@@ -34834,7 +35389,7 @@ const noImpureStateUpdater = defineRule({
34834
35389
  });
34835
35390
  //#endregion
34836
35391
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
34837
- const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
35392
+ const MESSAGE$22 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
34838
35393
  const REACT_USE_REF_OPTIONS = {
34839
35394
  allowGlobalReactNamespace: false,
34840
35395
  allowUnboundBareCalls: false
@@ -34935,7 +35490,7 @@ const noIndeterminateAttribute = defineRule({
34935
35490
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
34936
35491
  context.report({
34937
35492
  node: indeterminateAttribute,
34938
- message: MESSAGE$21
35493
+ message: MESSAGE$22
34939
35494
  });
34940
35495
  },
34941
35496
  CallExpression(node) {
@@ -34944,7 +35499,7 @@ const noIndeterminateAttribute = defineRule({
34944
35499
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
34945
35500
  context.report({
34946
35501
  node,
34947
- message: MESSAGE$21
35502
+ message: MESSAGE$22
34948
35503
  });
34949
35504
  }
34950
35505
  };
@@ -35067,10 +35622,10 @@ const isInsideInstanceField = (node) => {
35067
35622
  return false;
35068
35623
  };
35069
35624
  const isOneShotModuleInitialization = (node, context) => {
35070
- let functionNode = findEnclosingFunction(node);
35625
+ let functionNode = findEnclosingFunction$1(node);
35071
35626
  while (functionNode) {
35072
35627
  if (!executesDuringRender(functionNode, context.scopes)) return false;
35073
- functionNode = findEnclosingFunction(functionNode);
35628
+ functionNode = findEnclosingFunction$1(functionNode);
35074
35629
  }
35075
35630
  return !isInsideInstanceField(node);
35076
35631
  };
@@ -35232,7 +35787,7 @@ const noIsMounted = defineRule({
35232
35787
  });
35233
35788
  //#endregion
35234
35789
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
35235
- 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)`.";
35790
+ 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)`.";
35236
35791
  const isJsonMethodCall = (node, method) => {
35237
35792
  if (!isNodeOfType(node, "CallExpression")) return false;
35238
35793
  const callee = node.callee;
@@ -35296,13 +35851,13 @@ const noJsonParseStringifyClone = defineRule({
35296
35851
  if (isCatchParameterRoundTrip(firstArgument)) return;
35297
35852
  context.report({
35298
35853
  node,
35299
- message: MESSAGE$20
35854
+ message: MESSAGE$21
35300
35855
  });
35301
35856
  } })
35302
35857
  });
35303
35858
  //#endregion
35304
35859
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
35305
- const MESSAGE$19 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35860
+ const MESSAGE$20 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35306
35861
  const isJsxElementTypeReference = (node) => {
35307
35862
  if (!isNodeOfType(node, "TSTypeReference")) return false;
35308
35863
  const typeName = node.typeName;
@@ -35356,7 +35911,7 @@ const noJsxElementType = defineRule({
35356
35911
  if (isJsxImported) return;
35357
35912
  for (const typeAnnotation of flaggedAnnotations) context.report({
35358
35913
  node: typeAnnotation,
35359
- message: MESSAGE$19
35914
+ message: MESSAGE$20
35360
35915
  });
35361
35916
  }
35362
35917
  };
@@ -35588,6 +36143,221 @@ const noLegacyClassLifecycles = defineRule({
35588
36143
  }
35589
36144
  });
35590
36145
  //#endregion
36146
+ //#region src/plugin/utils/is-proven-react-class-component.ts
36147
+ const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
36148
+ const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
36149
+ const expression = stripParenExpression(node);
36150
+ if (isNodeOfType(expression, "MemberExpression")) {
36151
+ const propertyName = getStaticPropertyName(expression);
36152
+ const receiver = stripParenExpression(expression.object);
36153
+ return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
36154
+ }
36155
+ if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
36156
+ if (!isNodeOfType(expression, "Identifier")) return false;
36157
+ const symbol = scopes.symbolFor(expression);
36158
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
36159
+ visitedSymbolIds.add(symbol.id);
36160
+ if (isImportedFromReact(symbol)) {
36161
+ const importedName = getImportedName(symbol.declarationNode);
36162
+ return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
36163
+ }
36164
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
36165
+ return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
36166
+ };
36167
+ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36168
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
36169
+ visitedClassNodes.add(classNode);
36170
+ return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
36171
+ };
36172
+ //#endregion
36173
+ //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
36174
+ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
36175
+ if (!isFunctionLike$2(functionNode)) return false;
36176
+ let containsReactHookCall = false;
36177
+ walkAst(functionNode.body, (node) => {
36178
+ if (containsReactHookCall) return false;
36179
+ if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
36180
+ if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
36181
+ containsReactHookCall = true;
36182
+ return false;
36183
+ }
36184
+ });
36185
+ return containsReactHookCall;
36186
+ };
36187
+ //#endregion
36188
+ //#region src/plugin/utils/function-returns-props-children.ts
36189
+ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
36190
+ if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
36191
+ const firstParameter = stripParenExpression(functionNode.params[0]);
36192
+ const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
36193
+ const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
36194
+ const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
36195
+ if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
36196
+ for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
36197
+ const propertyValue = stripParenExpression(property.value);
36198
+ const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
36199
+ if (!isNodeOfType(childrenBinding, "Identifier")) continue;
36200
+ const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
36201
+ if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
36202
+ }
36203
+ }
36204
+ return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
36205
+ const candidate = stripParenExpression(expression);
36206
+ if (isNodeOfType(candidate, "Identifier")) {
36207
+ const symbol = scopes.symbolFor(candidate);
36208
+ return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
36209
+ }
36210
+ if (!isNodeOfType(candidate, "MemberExpression")) return false;
36211
+ if (getStaticPropertyName(candidate) !== "children") return false;
36212
+ const receiver = stripParenExpression(candidate.object);
36213
+ if (!isNodeOfType(receiver, "Identifier")) return false;
36214
+ const receiverSymbol = scopes.symbolFor(receiver);
36215
+ if (!receiverSymbol || !propsParameterSymbol) return false;
36216
+ return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
36217
+ }, controlFlow);
36218
+ };
36219
+ //#endregion
36220
+ //#region src/plugin/utils/function-returns-only-null.ts
36221
+ const isNullExpression = (expression) => {
36222
+ const candidate = stripParenExpression(expression);
36223
+ return isNodeOfType(candidate, "Literal") && candidate.value === null;
36224
+ };
36225
+ const functionReturnsOnlyNull = (functionNode) => {
36226
+ if (!isFunctionLike$2(functionNode)) return false;
36227
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
36228
+ const returnStatements = collectFunctionReturnStatements(functionNode);
36229
+ return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
36230
+ };
36231
+ //#endregion
36232
+ //#region src/plugin/utils/is-proven-styled-component-expression.ts
36233
+ const findFactoryRoot = (node) => {
36234
+ const candidate = stripParenExpression(node);
36235
+ if (isNodeOfType(candidate, "Identifier")) return candidate;
36236
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
36237
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
36238
+ return null;
36239
+ };
36240
+ const findFactoryPropertyName = (node) => {
36241
+ const candidate = stripParenExpression(node);
36242
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
36243
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
36244
+ return null;
36245
+ };
36246
+ const isProvenStyledComponentExpression = (expression, scopes) => {
36247
+ const candidate = stripParenExpression(expression);
36248
+ if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
36249
+ const factoryRoot = findFactoryRoot(candidate.tag);
36250
+ if (!factoryRoot) return false;
36251
+ const factoryPropertyName = findFactoryPropertyName(candidate.tag);
36252
+ if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
36253
+ const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
36254
+ if (!symbol || symbol.kind !== "import") return false;
36255
+ if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
36256
+ const importDeclaration = symbol.declarationNode.parent;
36257
+ return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
36258
+ };
36259
+ //#endregion
36260
+ //#region src/plugin/utils/is-proven-react-component-symbol.ts
36261
+ const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
36262
+ const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
36263
+ const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
36264
+ const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36265
+ const candidate = stripParenExpression(expression);
36266
+ if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
36267
+ if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
36268
+ if (isProvenStyledComponentExpression(candidate, scopes)) return true;
36269
+ if (isNodeOfType(candidate, "Identifier")) {
36270
+ const symbol = scopes.symbolFor(candidate);
36271
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
36272
+ visitedSymbolIds.add(symbol.id);
36273
+ if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
36274
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
36275
+ return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
36276
+ }
36277
+ if (!isNodeOfType(candidate, "CallExpression")) return false;
36278
+ if (!hasStableCallTarget(candidate, scopes)) return false;
36279
+ const factoryCallee = stripParenExpression(candidate.callee);
36280
+ if (isNodeOfType(factoryCallee, "Identifier") && isDefaultImportFromModule(factoryCallee, factoryCallee.name, "create-react-class") || isReactApiCall(candidate, LEGACY_REACT_COMPONENT_FACTORY_NAMES, scopes)) return true;
36281
+ if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
36282
+ const wrappedComponent = candidate.arguments[0];
36283
+ return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
36284
+ }
36285
+ if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
36286
+ const factory = candidate.arguments[0];
36287
+ if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
36288
+ const unwrappedFactory = stripParenExpression(factory);
36289
+ if (!isInlineFunctionExpression(unwrappedFactory)) return false;
36290
+ if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
36291
+ const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
36292
+ const returnedExpression = returnStatements[0]?.argument;
36293
+ return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
36294
+ };
36295
+ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
36296
+ const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
36297
+ for (const candidateSymbol of candidateSymbols) {
36298
+ if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
36299
+ if (isComponentDeclaration(candidateSymbol.declarationNode)) {
36300
+ if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
36301
+ continue;
36302
+ }
36303
+ const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
36304
+ if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
36305
+ if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
36306
+ continue;
36307
+ }
36308
+ if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
36309
+ if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
36310
+ continue;
36311
+ }
36312
+ if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
36313
+ }
36314
+ return false;
36315
+ };
36316
+ //#endregion
36317
+ //#region src/plugin/utils/symbol-has-react-component-type-annotation.ts
36318
+ const REACT_COMPONENT_TYPE_NAMES = new Set([
36319
+ "ComponentClass",
36320
+ "ComponentType",
36321
+ "FC",
36322
+ "FunctionComponent"
36323
+ ]);
36324
+ const findVisibleSymbol = (identifier, scopes) => {
36325
+ if (!isNodeOfType(identifier, "Identifier")) return null;
36326
+ let scope = scopes.scopeFor(identifier);
36327
+ while (true) {
36328
+ const symbol = scope.symbolsByName.get(identifier.name);
36329
+ if (symbol) return symbol;
36330
+ if (!scope.parent) return null;
36331
+ scope = scope.parent;
36332
+ }
36333
+ };
36334
+ const isReactNamespaceType = (identifier, scopes) => {
36335
+ const symbol = findVisibleSymbol(identifier, scopes);
36336
+ return Boolean(symbol && isImportedFromReact(symbol) && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
36337
+ };
36338
+ const isReactComponentType = (typeNode, scopes, visitedSymbolIds) => {
36339
+ if (!typeNode) return false;
36340
+ if (isNodeOfType(typeNode, "TSTypeAnnotation")) return isReactComponentType(typeNode.typeAnnotation, scopes, visitedSymbolIds);
36341
+ if (isNodeOfType(typeNode, "TSIntersectionType")) return (typeNode.types ?? []).some((member) => isReactComponentType(member, scopes, visitedSymbolIds));
36342
+ if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
36343
+ const typeName = typeNode.typeName;
36344
+ 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);
36345
+ if (!isNodeOfType(typeName, "Identifier")) return false;
36346
+ const typeSymbol = findVisibleSymbol(typeName, scopes);
36347
+ if (!typeSymbol || visitedSymbolIds.has(typeSymbol.id)) return false;
36348
+ if (isImportedFromReact(typeSymbol)) {
36349
+ const importedName = getImportedName(typeSymbol.declarationNode);
36350
+ return Boolean(importedName && REACT_COMPONENT_TYPE_NAMES.has(importedName));
36351
+ }
36352
+ if (!isNodeOfType(typeSymbol.declarationNode, "TSTypeAliasDeclaration")) return false;
36353
+ visitedSymbolIds.add(typeSymbol.id);
36354
+ return isReactComponentType(typeSymbol.declarationNode.typeAnnotation, scopes, visitedSymbolIds);
36355
+ };
36356
+ const symbolHasReactComponentTypeAnnotation = (symbol, scopes) => {
36357
+ const bindingIdentifier = symbol.bindingIdentifier;
36358
+ return isNodeOfType(bindingIdentifier, "Identifier") && isReactComponentType(bindingIdentifier.typeAnnotation, scopes, /* @__PURE__ */ new Set());
36359
+ };
36360
+ //#endregion
35591
36361
  //#region src/plugin/rules/architecture/no-legacy-context-api.ts
35592
36362
  const LEGACY_CONTEXT_NAMES = new Set([
35593
36363
  "childContextTypes",
@@ -35598,15 +36368,6 @@ const buildLegacyContextMessage = (memberName) => {
35598
36368
  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.`;
35599
36369
  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.";
35600
36370
  };
35601
- const isInsideClassBody = (node) => {
35602
- let current = node.parent;
35603
- while (current) {
35604
- if (isNodeOfType(current, "ClassBody")) return true;
35605
- if (isFunctionLike$2(current)) return false;
35606
- current = current.parent;
35607
- }
35608
- return false;
35609
- };
35610
36371
  const noLegacyContextApi = defineRule({
35611
36372
  id: "no-legacy-context-api",
35612
36373
  title: "Legacy context API",
@@ -35621,6 +36382,7 @@ const noLegacyContextApi = defineRule({
35621
36382
  if (!isNodeOfType(memberNode, "MethodDefinition") && !isNodeOfType(memberNode, "PropertyDefinition")) return;
35622
36383
  if (!isNodeOfType(memberNode.key, "Identifier")) return;
35623
36384
  if (!LEGACY_CONTEXT_NAMES.has(memberNode.key.name)) return;
36385
+ if (memberNode.key.name === "getChildContext" ? memberNode.static : !memberNode.static) return;
35624
36386
  context.report({
35625
36387
  node: memberNode.key,
35626
36388
  message: buildLegacyContextMessage(memberNode.key.name)
@@ -35628,6 +36390,8 @@ const noLegacyContextApi = defineRule({
35628
36390
  };
35629
36391
  return {
35630
36392
  ClassBody(node) {
36393
+ const classNode = node.parent;
36394
+ if (!classNode || !isProvenReactClassComponent(classNode, context.scopes)) return;
35631
36395
  for (const member of node.body ?? []) checkMember(member);
35632
36396
  },
35633
36397
  AssignmentExpression(node) {
@@ -35637,9 +36401,11 @@ const noLegacyContextApi = defineRule({
35637
36401
  if (left.computed) return;
35638
36402
  if (!isNodeOfType(left.property, "Identifier")) return;
35639
36403
  if (!LEGACY_CONTEXT_NAMES.has(left.property.name)) return;
35640
- if (!isNodeOfType(left.object, "Identifier")) return;
35641
- if (!isUppercaseName(left.object.name)) return;
35642
- if (isInsideClassBody(node)) return;
36404
+ if (left.property.name === "getChildContext") return;
36405
+ const component = stripParenExpression(left.object);
36406
+ if (!isNodeOfType(component, "Identifier")) return;
36407
+ const symbol = context.scopes.symbolFor(component);
36408
+ if (!symbol || !isProvenReactComponentSymbol(symbol, context.scopes, context.cfg, component) && (hasSymbolWriteBefore(symbol, component, context.scopes) || !symbolHasReactComponentTypeAnnotation(symbol, context.scopes))) return;
35643
36409
  context.report({
35644
36410
  node: left,
35645
36411
  message: buildLegacyContextMessage(left.property.name)
@@ -35843,10 +36609,10 @@ const getMethodCalls = (functionNode, scopes) => {
35843
36609
  const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, scopes, visitedSymbolIds, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
35844
36610
  if (visitedFunctionNodes.has(functionNode)) return false;
35845
36611
  visitedFunctionNodes.add(functionNode);
35846
- const usageFunction = findEnclosingFunction(usageNode);
36612
+ const usageFunction = findEnclosingFunction$1(usageNode);
35847
36613
  const immediateCall = getDirectCallForExpression(functionNode);
35848
36614
  if (immediateCall) {
35849
- const immediateCallFunction = findEnclosingFunction(immediateCall);
36615
+ const immediateCallFunction = findEnclosingFunction$1(immediateCall);
35850
36616
  if (immediateCallFunction === usageFunction) {
35851
36617
  const immediateCallStart = getRangeStart(immediateCall);
35852
36618
  return immediateCallStart === null || immediateCallStart < usageBoundary;
@@ -35855,7 +36621,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
35855
36621
  return isFunctionInvokedBeforeUsage(immediateCallFunction, usageNode, usageBoundary, scopes, visitedSymbolIds, new Set(visitedFunctionNodes));
35856
36622
  }
35857
36623
  for (const methodCall of getMethodCalls(functionNode, scopes)) {
35858
- const methodCallFunction = findEnclosingFunction(methodCall);
36624
+ const methodCallFunction = findEnclosingFunction$1(methodCall);
35859
36625
  if (methodCallFunction === usageFunction) {
35860
36626
  const methodCallStart = getRangeStart(methodCall);
35861
36627
  if (methodCallStart === null || methodCallStart < usageBoundary) return true;
@@ -35878,7 +36644,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
35878
36644
  if (scopes.symbolFor(child)?.declarationNode !== symbol.declarationNode) return;
35879
36645
  const call = getDirectCallForExpression(child);
35880
36646
  if (!call) return false;
35881
- const callFunction = findEnclosingFunction(call);
36647
+ const callFunction = findEnclosingFunction$1(call);
35882
36648
  if (callFunction === usageFunction) {
35883
36649
  const callStart = getRangeStart(call);
35884
36650
  wasInvokedBeforeUsage = callStart === null || callStart < usageBoundary;
@@ -35909,14 +36675,14 @@ const wasMutatedBeforeUsage = (symbol, usageNode, readNode, scopes, visitedMutat
35909
36675
  visitedMutationSymbolIds.add(symbol.id);
35910
36676
  const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, readNode) : inheritedUsageBoundary;
35911
36677
  if (typeof usageBoundary !== "number") return true;
35912
- const usageFunction = findEnclosingFunction(usageNode);
36678
+ const usageFunction = findEnclosingFunction$1(usageNode);
35913
36679
  return symbol.references.some((reference) => {
35914
36680
  const referenceStart = getRangeStart(reference.identifier);
35915
36681
  const simpleAlias = getSimpleAlias(reference.identifier, scopes);
35916
36682
  if (simpleAlias) return wasMutatedBeforeUsage(simpleAlias.symbol, usageNode, readNodesBySymbolId.get(simpleAlias.symbol.id) ?? simpleAlias.readNode, scopes, new Set(visitedMutationSymbolIds), usageBoundary, readNodesBySymbolId);
35917
36683
  if (!isPotentialMutationReference(reference.identifier, readNode)) return false;
35918
36684
  if (referenceStart === null) return true;
35919
- const mutationFunction = findEnclosingFunction(reference.identifier);
36685
+ const mutationFunction = findEnclosingFunction$1(reference.identifier);
35920
36686
  if (mutationFunction === usageFunction) return referenceStart < usageBoundary;
35921
36687
  if (!mutationFunction) return usageFunction !== null;
35922
36688
  if (usageFunction && isAstDescendant(usageFunction, mutationFunction)) return true;
@@ -36461,7 +37227,7 @@ const noMoment = defineRule({
36461
37227
  });
36462
37228
  //#endregion
36463
37229
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
36464
- const MESSAGE$18 = "This file declares several components, so each component is harder to find, test, and change.";
37230
+ const MESSAGE$19 = "This file declares several components, so each component is harder to find, test, and change.";
36465
37231
  const resolveSettings$16 = (settings) => {
36466
37232
  const reactDoctor = settings?.["react-doctor"];
36467
37233
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -36498,13 +37264,6 @@ const isReactNamespaceExpression = (node, scopes) => {
36498
37264
  return isReactNamespaceImport(node, scopes);
36499
37265
  };
36500
37266
  const isReactHocMemberReference = (node, scopes) => Boolean(isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.property, "Identifier") && REACT_HOC_NAMES.has(node.property.name) && isReactNamespaceExpression(node.object, scopes));
36501
- const getDestructuredPropertyName$1 = (symbol) => {
36502
- const property = symbol.bindingIdentifier.parent;
36503
- if (!property || !isNodeOfType(property, "Property") || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
36504
- if (isNodeOfType(property.key, "Identifier") && !property.computed) return property.key.name;
36505
- if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
36506
- return null;
36507
- };
36508
37267
  const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
36509
37268
  if (visitedSymbolIds.has(symbol.id)) return false;
36510
37269
  visitedSymbolIds.add(symbol.id);
@@ -36514,7 +37273,7 @@ const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
36514
37273
  }
36515
37274
  const init = symbol.initializer;
36516
37275
  if (!init) return false;
36517
- const destructuredPropertyName = getDestructuredPropertyName$1(symbol);
37276
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
36518
37277
  if (destructuredPropertyName && REACT_HOC_NAMES.has(destructuredPropertyName) && isReactNamespaceExpression(init, scopes)) return true;
36519
37278
  if (isReactHocMemberReference(init, scopes)) return true;
36520
37279
  if (isNodeOfType(init, "Identifier")) {
@@ -36820,7 +37579,7 @@ const noMultiComp = defineRule({
36820
37579
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
36821
37580
  for (const component of flagged.slice(1)) context.report({
36822
37581
  node: component.reportNode,
36823
- message: MESSAGE$18
37582
+ message: MESSAGE$19
36824
37583
  });
36825
37584
  } };
36826
37585
  }
@@ -37033,7 +37792,7 @@ const resolveReducerFunction = (node, currentFilename) => {
37033
37792
  };
37034
37793
  //#endregion
37035
37794
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
37036
- const MESSAGE$17 = "This reducer changes state in place, so your update is silently skipped.";
37795
+ const MESSAGE$18 = "This reducer changes state in place, so your update is silently skipped.";
37037
37796
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
37038
37797
  "copyWithin",
37039
37798
  "fill",
@@ -37242,7 +38001,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
37242
38001
  reportedNodes.add(options.crossFileConsumerCallSite);
37243
38002
  context.report({
37244
38003
  node: options.crossFileConsumerCallSite,
37245
- message: `${MESSAGE$17} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
38004
+ message: `${MESSAGE$18} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
37246
38005
  });
37247
38006
  return;
37248
38007
  }
@@ -37251,7 +38010,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
37251
38010
  reportedNodes.add(mutation.node);
37252
38011
  context.report({
37253
38012
  node: mutation.node,
37254
- message: MESSAGE$17
38013
+ message: MESSAGE$18
37255
38014
  });
37256
38015
  }
37257
38016
  };
@@ -37658,7 +38417,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
37658
38417
  });
37659
38418
  //#endregion
37660
38419
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
37661
- const MESSAGE$16 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
38420
+ const MESSAGE$17 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
37662
38421
  const KEYBOARD_HANDLER_PROP_NAMES = [
37663
38422
  "onKeyDown",
37664
38423
  "onKeyUp",
@@ -37777,7 +38536,7 @@ const noNoninteractiveTabindex = defineRule({
37777
38536
  if (numeric === null) {
37778
38537
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
37779
38538
  node: tabIndex,
37780
- message: MESSAGE$16
38539
+ message: MESSAGE$17
37781
38540
  });
37782
38541
  return;
37783
38542
  }
@@ -37799,7 +38558,7 @@ const noNoninteractiveTabindex = defineRule({
37799
38558
  if (!roleAttribute) {
37800
38559
  context.report({
37801
38560
  node: tabIndex,
37802
- message: MESSAGE$16
38561
+ message: MESSAGE$17
37803
38562
  });
37804
38563
  return;
37805
38564
  }
@@ -37813,7 +38572,7 @@ const noNoninteractiveTabindex = defineRule({
37813
38572
  }
37814
38573
  context.report({
37815
38574
  node: tabIndex,
37816
- message: MESSAGE$16
38575
+ message: MESSAGE$17
37817
38576
  });
37818
38577
  } };
37819
38578
  }
@@ -38268,12 +39027,6 @@ const getDeclarationKind = (declarator) => {
38268
39027
  return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
38269
39028
  };
38270
39029
  const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
38271
- const getDestructuredPropertyName = (bindingIdentifier) => {
38272
- const property = bindingIdentifier.parent;
38273
- if (!property || !isNodeOfType(property, "Property")) return null;
38274
- if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
38275
- return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
38276
- };
38277
39030
  const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
38278
39031
  const unwrappedExpression = stripParenExpression(expression);
38279
39032
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -38283,7 +39036,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
38283
39036
  if (isProp(analysis, callbackReference)) {
38284
39037
  if (isWholePropsObjectReference(analysis, callbackReference)) return null;
38285
39038
  const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
38286
- return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
39039
+ return (bindingIdentifier && getDestructuredBindingPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
38287
39040
  }
38288
39041
  if (hasMutableBindingWrite(callbackReference)) return null;
38289
39042
  const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
@@ -38293,7 +39046,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
38293
39046
  visitedVariables.add(callbackVariable);
38294
39047
  if (isNodeOfType(declarator.id, "ObjectPattern")) {
38295
39048
  const bindingIdentifier = callbackVariable.defs[0]?.name;
38296
- const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
39049
+ const propertyName = bindingIdentifier ? getDestructuredBindingPropertyName(bindingIdentifier) : null;
38297
39050
  const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
38298
39051
  return propertyName && propsReference ? propertyName : null;
38299
39052
  }
@@ -38396,7 +39149,7 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
38396
39149
  const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
38397
39150
  if (!bindingProvenance) return null;
38398
39151
  const { refCall, variables } = bindingProvenance;
38399
- const componentFunction = findEnclosingFunction(effectCall);
39152
+ const componentFunction = findEnclosingFunction$1(effectCall);
38400
39153
  if (!componentFunction || !isFunctionLike$2(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
38401
39154
  if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
38402
39155
  const callbackPropNames = /* @__PURE__ */ new Set();
@@ -39232,174 +39985,6 @@ const noPropCallbackInRender = defineRule({
39232
39985
  } })
39233
39986
  });
39234
39987
  //#endregion
39235
- //#region src/plugin/utils/is-proven-react-class-component.ts
39236
- const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
39237
- const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
39238
- const expression = stripParenExpression(node);
39239
- if (isNodeOfType(expression, "MemberExpression")) {
39240
- const propertyName = getStaticPropertyName(expression);
39241
- const receiver = stripParenExpression(expression.object);
39242
- return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
39243
- }
39244
- if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
39245
- if (!isNodeOfType(expression, "Identifier")) return false;
39246
- const symbol = scopes.symbolFor(expression);
39247
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
39248
- visitedSymbolIds.add(symbol.id);
39249
- if (isImportedFromReact(symbol)) {
39250
- const importedName = getImportedName(symbol.declarationNode);
39251
- return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
39252
- }
39253
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
39254
- return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
39255
- };
39256
- const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39257
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
39258
- visitedClassNodes.add(classNode);
39259
- return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
39260
- };
39261
- //#endregion
39262
- //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
39263
- const functionContainsProvenReactHookCall = (functionNode, scopes) => {
39264
- if (!isFunctionLike$2(functionNode)) return false;
39265
- let containsReactHookCall = false;
39266
- walkAst(functionNode.body, (node) => {
39267
- if (containsReactHookCall) return false;
39268
- if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
39269
- if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
39270
- containsReactHookCall = true;
39271
- return false;
39272
- }
39273
- });
39274
- return containsReactHookCall;
39275
- };
39276
- //#endregion
39277
- //#region src/plugin/utils/function-returns-props-children.ts
39278
- const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
39279
- if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
39280
- const firstParameter = stripParenExpression(functionNode.params[0]);
39281
- const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
39282
- const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
39283
- const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
39284
- if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
39285
- for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
39286
- const propertyValue = stripParenExpression(property.value);
39287
- const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
39288
- if (!isNodeOfType(childrenBinding, "Identifier")) continue;
39289
- const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
39290
- if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
39291
- }
39292
- }
39293
- return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
39294
- const candidate = stripParenExpression(expression);
39295
- if (isNodeOfType(candidate, "Identifier")) {
39296
- const symbol = scopes.symbolFor(candidate);
39297
- return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
39298
- }
39299
- if (!isNodeOfType(candidate, "MemberExpression")) return false;
39300
- if (getStaticPropertyName(candidate) !== "children") return false;
39301
- const receiver = stripParenExpression(candidate.object);
39302
- if (!isNodeOfType(receiver, "Identifier")) return false;
39303
- const receiverSymbol = scopes.symbolFor(receiver);
39304
- if (!receiverSymbol || !propsParameterSymbol) return false;
39305
- return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
39306
- }, controlFlow);
39307
- };
39308
- //#endregion
39309
- //#region src/plugin/utils/function-returns-only-null.ts
39310
- const isNullExpression = (expression) => {
39311
- const candidate = stripParenExpression(expression);
39312
- return isNodeOfType(candidate, "Literal") && candidate.value === null;
39313
- };
39314
- const functionReturnsOnlyNull = (functionNode) => {
39315
- if (!isFunctionLike$2(functionNode)) return false;
39316
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
39317
- const returnStatements = collectFunctionReturnStatements(functionNode);
39318
- return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
39319
- };
39320
- //#endregion
39321
- //#region src/plugin/utils/is-proven-styled-component-expression.ts
39322
- const findFactoryRoot = (node) => {
39323
- const candidate = stripParenExpression(node);
39324
- if (isNodeOfType(candidate, "Identifier")) return candidate;
39325
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
39326
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
39327
- return null;
39328
- };
39329
- const findFactoryPropertyName = (node) => {
39330
- const candidate = stripParenExpression(node);
39331
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
39332
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
39333
- return null;
39334
- };
39335
- const isProvenStyledComponentExpression = (expression, scopes) => {
39336
- const candidate = stripParenExpression(expression);
39337
- if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
39338
- const factoryRoot = findFactoryRoot(candidate.tag);
39339
- if (!factoryRoot) return false;
39340
- const factoryPropertyName = findFactoryPropertyName(candidate.tag);
39341
- if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
39342
- const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
39343
- if (!symbol || symbol.kind !== "import") return false;
39344
- if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
39345
- const importDeclaration = symbol.declarationNode.parent;
39346
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
39347
- };
39348
- //#endregion
39349
- //#region src/plugin/utils/is-proven-react-component-symbol.ts
39350
- const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
39351
- const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39352
- const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39353
- const candidate = stripParenExpression(expression);
39354
- if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
39355
- if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
39356
- if (isProvenStyledComponentExpression(candidate, scopes)) return true;
39357
- if (isNodeOfType(candidate, "Identifier")) {
39358
- const symbol = scopes.symbolFor(candidate);
39359
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
39360
- visitedSymbolIds.add(symbol.id);
39361
- if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
39362
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
39363
- return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
39364
- }
39365
- if (!isNodeOfType(candidate, "CallExpression")) return false;
39366
- if (!hasStableCallTarget(candidate, scopes)) return false;
39367
- if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
39368
- const wrappedComponent = candidate.arguments[0];
39369
- return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
39370
- }
39371
- if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
39372
- const factory = candidate.arguments[0];
39373
- if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
39374
- const unwrappedFactory = stripParenExpression(factory);
39375
- if (!isInlineFunctionExpression(unwrappedFactory)) return false;
39376
- if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
39377
- const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
39378
- const returnedExpression = returnStatements[0]?.argument;
39379
- return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
39380
- };
39381
- const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
39382
- const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
39383
- for (const candidateSymbol of candidateSymbols) {
39384
- if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
39385
- if (isComponentDeclaration(candidateSymbol.declarationNode)) {
39386
- if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
39387
- continue;
39388
- }
39389
- const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
39390
- if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
39391
- if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
39392
- continue;
39393
- }
39394
- if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
39395
- if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
39396
- continue;
39397
- }
39398
- if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
39399
- }
39400
- return false;
39401
- };
39402
- //#endregion
39403
39988
  //#region src/plugin/rules/architecture/no-prop-types.ts
39404
39989
  const PROP_TYPES_PROPERTY = "propTypes";
39405
39990
  const isPropTypesKey = (key, computed) => {
@@ -39552,7 +40137,7 @@ const isModuleScopedBinding = (identifier) => {
39552
40137
  const binding = findVariableInitializer(identifier, identifier.name);
39553
40138
  if (!binding) return false;
39554
40139
  if (isNodeOfType(binding.scopeOwner, "Program")) return true;
39555
- if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction(binding.scopeOwner) === null;
40140
+ if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction$1(binding.scopeOwner) === null;
39556
40141
  return false;
39557
40142
  };
39558
40143
  const looksLikeFreshUpdateExpression = (expression) => {
@@ -39607,7 +40192,7 @@ const noRandomKey = defineRule({
39607
40192
  });
39608
40193
  //#endregion
39609
40194
  //#region src/plugin/rules/react-builtins/no-react-children.ts
39610
- const MESSAGE$15 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
40195
+ const MESSAGE$16 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
39611
40196
  const isChildrenIdentifier = (node, contextNode) => {
39612
40197
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
39613
40198
  return isImportedFromModule(contextNode, "Children", "react");
@@ -39633,13 +40218,13 @@ const noReactChildren = defineRule({
39633
40218
  if (isChildrenIdentifier(memberObject, node)) {
39634
40219
  context.report({
39635
40220
  node: calleeOuter,
39636
- message: MESSAGE$15
40221
+ message: MESSAGE$16
39637
40222
  });
39638
40223
  return;
39639
40224
  }
39640
40225
  if (isReactNamespaceMember(memberObject, node)) context.report({
39641
40226
  node: calleeOuter,
39642
- message: MESSAGE$15
40227
+ message: MESSAGE$16
39643
40228
  });
39644
40229
  } })
39645
40230
  });
@@ -40320,7 +40905,7 @@ const noRenderPropChildren = defineRule({
40320
40905
  });
40321
40906
  //#endregion
40322
40907
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
40323
- const MESSAGE$14 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40908
+ const MESSAGE$15 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40324
40909
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
40325
40910
  const isUsedAsReturnValue = (parent) => {
40326
40911
  if (!parent) return false;
@@ -40338,7 +40923,7 @@ const noRenderReturnValue = defineRule({
40338
40923
  if (!isUsedAsReturnValue(node.parent)) return;
40339
40924
  context.report({
40340
40925
  node: node.callee,
40341
- message: MESSAGE$14
40926
+ message: MESSAGE$15
40342
40927
  });
40343
40928
  } })
40344
40929
  });
@@ -41147,7 +41732,7 @@ const noSelfUpdatingEffect = defineRule({
41147
41732
  });
41148
41733
  //#endregion
41149
41734
  //#region src/plugin/rules/react-builtins/no-set-state.ts
41150
- const MESSAGE$13 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41735
+ const MESSAGE$14 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41151
41736
  const noSetState = defineRule({
41152
41737
  id: "no-set-state",
41153
41738
  title: "Local class state forbidden",
@@ -41162,7 +41747,7 @@ const noSetState = defineRule({
41162
41747
  if (!getParentComponent(node)) return;
41163
41748
  context.report({
41164
41749
  node: node.callee,
41165
- message: MESSAGE$13
41750
+ message: MESSAGE$14
41166
41751
  });
41167
41752
  } })
41168
41753
  });
@@ -41469,9 +42054,9 @@ const isReturnedFromAnyEffectInScope = (functionNode, ownerScope) => {
41469
42054
  return isReturnedFromEffect;
41470
42055
  };
41471
42056
  const isInsideEffectCleanupReturn = (node, ownerScope) => {
41472
- let functionNode = findEnclosingFunction(node);
42057
+ let functionNode = findEnclosingFunction$1(node);
41473
42058
  while (functionNode) {
41474
- const outerFunction = findEnclosingFunction(functionNode);
42059
+ const outerFunction = findEnclosingFunction$1(functionNode);
41475
42060
  if (outerFunction && isEffectCallbackFunction(outerFunction) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
41476
42061
  if (isReturnedFromAnyEffectInScope(functionNode, ownerScope)) return true;
41477
42062
  functionNode = outerFunction;
@@ -41479,7 +42064,7 @@ const isInsideEffectCleanupReturn = (node, ownerScope) => {
41479
42064
  return false;
41480
42065
  };
41481
42066
  const hasRefCurrentReassignmentAfterClear = (clearCall, refName) => {
41482
- const enclosingFunction = findEnclosingFunction(clearCall);
42067
+ const enclosingFunction = findEnclosingFunction$1(clearCall);
41483
42068
  if (!isFunctionLike$2(enclosingFunction)) return false;
41484
42069
  if (!isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
41485
42070
  const clearStart = getRangeStart(clearCall);
@@ -41532,7 +42117,7 @@ const isAbstractRole = (openingElement, settings) => {
41532
42117
  };
41533
42118
  //#endregion
41534
42119
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
41535
- 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.";
42120
+ 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.";
41536
42121
  const DEFAULT_HANDLERS = [
41537
42122
  "onClick",
41538
42123
  "onMouseDown",
@@ -41647,7 +42232,7 @@ const noStaticElementInteractions = defineRule({
41647
42232
  if (!roleAttribute || !roleAttribute.value) {
41648
42233
  context.report({
41649
42234
  node: node.name,
41650
- message: MESSAGE$12
42235
+ message: MESSAGE$13
41651
42236
  });
41652
42237
  return;
41653
42238
  }
@@ -41657,7 +42242,7 @@ const noStaticElementInteractions = defineRule({
41657
42242
  if (isRecognizedRoleString(attributeValue.value)) return;
41658
42243
  context.report({
41659
42244
  node: node.name,
41660
- message: MESSAGE$12
42245
+ message: MESSAGE$13
41661
42246
  });
41662
42247
  return;
41663
42248
  }
@@ -41665,7 +42250,7 @@ const noStaticElementInteractions = defineRule({
41665
42250
  if (!settings.allowExpressionValues) {
41666
42251
  context.report({
41667
42252
  node: node.name,
41668
- message: MESSAGE$12
42253
+ message: MESSAGE$13
41669
42254
  });
41670
42255
  return;
41671
42256
  }
@@ -41673,7 +42258,7 @@ const noStaticElementInteractions = defineRule({
41673
42258
  if (isStaticNullishExpression(expression)) {
41674
42259
  context.report({
41675
42260
  node: node.name,
41676
- message: MESSAGE$12
42261
+ message: MESSAGE$13
41677
42262
  });
41678
42263
  return;
41679
42264
  }
@@ -41683,7 +42268,7 @@ const noStaticElementInteractions = defineRule({
41683
42268
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
41684
42269
  context.report({
41685
42270
  node: node.name,
41686
- message: MESSAGE$12
42271
+ message: MESSAGE$13
41687
42272
  });
41688
42273
  return;
41689
42274
  }
@@ -41691,7 +42276,7 @@ const noStaticElementInteractions = defineRule({
41691
42276
  }
41692
42277
  context.report({
41693
42278
  node: node.name,
41694
- message: MESSAGE$12
42279
+ message: MESSAGE$13
41695
42280
  });
41696
42281
  } };
41697
42282
  }
@@ -41797,7 +42382,7 @@ const noStringRefs = defineRule({
41797
42382
  });
41798
42383
  //#endregion
41799
42384
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
41800
- 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)`).";
42385
+ 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)`).";
41801
42386
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
41802
42387
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
41803
42388
  const noSyncXhr = defineRule({
@@ -41818,7 +42403,7 @@ const noSyncXhr = defineRule({
41818
42403
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
41819
42404
  context.report({
41820
42405
  node,
41821
- message: MESSAGE$11
42406
+ message: MESSAGE$12
41822
42407
  });
41823
42408
  };
41824
42409
  return {
@@ -41843,7 +42428,7 @@ const noSyncXhr = defineRule({
41843
42428
  });
41844
42429
  //#endregion
41845
42430
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
41846
- const MESSAGE$10 = "This value is `undefined` because function components have no `this`.";
42431
+ const MESSAGE$11 = "This value is `undefined` because function components have no `this`.";
41847
42432
  const isInsideClassMethod = (node, customClassFactoryNames) => {
41848
42433
  let ancestor = node.parent;
41849
42434
  while (ancestor) {
@@ -41931,7 +42516,7 @@ const noThisInSfc = defineRule({
41931
42516
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
41932
42517
  context.report({
41933
42518
  node,
41934
- message: MESSAGE$10
42519
+ message: MESSAGE$11
41935
42520
  });
41936
42521
  } };
41937
42522
  }
@@ -42309,7 +42894,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
42309
42894
  return false;
42310
42895
  };
42311
42896
  const isAfterAvailabilityEarlyExit = (node, componentOrHookNode, browserGlobalName, context) => {
42312
- const enclosingFunction = findEnclosingFunction(node);
42897
+ const enclosingFunction = findEnclosingFunction$1(node);
42313
42898
  if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$2(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
42314
42899
  let currentNode = node;
42315
42900
  while (currentNode !== enclosingFunction) {
@@ -43576,7 +44161,11 @@ const resolveSettings$8 = (settings) => {
43576
44161
  propNamePattern: ruleSettings.propNamePattern ?? "render*"
43577
44162
  };
43578
44163
  };
43579
- const expressionContainsJsxOrCreateElement = (root) => {
44164
+ const isReactCreateElementCall = (node, scopes) => isReactApiCall(node, "createElement", scopes, {
44165
+ allowGlobalReactNamespace: true,
44166
+ resolveNamedAliases: true
44167
+ });
44168
+ const expressionContainsJsxOrCreateElement = (root, scopes) => {
43580
44169
  let found = false;
43581
44170
  walkAst(root, (node) => {
43582
44171
  if (found) return false;
@@ -43585,17 +44174,17 @@ const expressionContainsJsxOrCreateElement = (root) => {
43585
44174
  found = true;
43586
44175
  return false;
43587
44176
  }
43588
- if (isNodeOfType(node, "CallExpression") && isCreateElementCall(node)) {
44177
+ if (isNodeOfType(node, "CallExpression") && isReactCreateElementCall(node, scopes)) {
43589
44178
  found = true;
43590
44179
  return false;
43591
44180
  }
43592
44181
  });
43593
44182
  return found;
43594
44183
  };
43595
- const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement, controlFlow);
43596
- const isReactClassComponent = (classNode) => {
44184
+ const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => expressionContainsJsxOrCreateElement(expression, scopes), controlFlow);
44185
+ const isReactClassComponent = (classNode, scopes) => {
43597
44186
  if (isEs6Component(classNode)) return true;
43598
- return expressionContainsJsxOrCreateElement(classNode);
44187
+ return expressionContainsJsxOrCreateElement(classNode, scopes);
43599
44188
  };
43600
44189
  const findEnclosingComponent = (node, scopes, controlFlow) => {
43601
44190
  let walker = node.parent;
@@ -43612,7 +44201,7 @@ const findEnclosingComponent = (node, scopes, controlFlow) => {
43612
44201
  };
43613
44202
  }
43614
44203
  if (isNodeOfType(walker, "ClassDeclaration") || isNodeOfType(walker, "ClassExpression")) {
43615
- if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker)) return {
44204
+ if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker, scopes)) return {
43616
44205
  component: walker,
43617
44206
  name: walker.id.name
43618
44207
  };
@@ -43701,12 +44290,12 @@ const isObjectCallbackCandidate = (node) => {
43701
44290
  }
43702
44291
  return false;
43703
44292
  };
43704
- const hocCallContainsComponent = (call) => {
44293
+ const hocCallContainsComponent = (call, scopes) => {
43705
44294
  const firstArgument = call.arguments[0];
43706
44295
  if (!firstArgument) return false;
43707
- if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument);
43708
- if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument);
43709
- return expressionContainsJsxOrCreateElement(firstArgument);
44296
+ if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument, scopes);
44297
+ if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument, scopes);
44298
+ return expressionContainsJsxOrCreateElement(firstArgument, scopes);
43710
44299
  };
43711
44300
  const isFirstArgumentOfHocCall = (node) => {
43712
44301
  const parent = node.parent;
@@ -43739,19 +44328,35 @@ const isReturnOfMapCallback = (node) => {
43739
44328
  }
43740
44329
  return false;
43741
44330
  };
43742
- const isCloneElementCall = (node) => {
43743
- if (!isNodeOfType(node, "CallExpression")) return false;
43744
- const callee = node.callee;
43745
- if (isNodeOfType(callee, "Identifier")) return callee.name === "cloneElement";
43746
- return isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "cloneElement";
43747
- };
43748
44331
  const TS_VALUE_PASSTHROUGH_TYPES = new Set([
43749
44332
  "TSAsExpression",
43750
44333
  "TSNonNullExpression",
43751
44334
  "TSSatisfiesExpression",
43752
44335
  "TSTypeAssertion"
43753
44336
  ]);
43754
- const isRenderFlowingReadReference = (identifier) => {
44337
+ const ELEMENT_TYPE_PROP_NAMES = new Set([
44338
+ "as",
44339
+ "body",
44340
+ "calendarcontainer",
44341
+ "component",
44342
+ "fallback",
44343
+ "tooltip"
44344
+ ]);
44345
+ const isElementTypeJsxAttribute = (node) => {
44346
+ if (!isNodeOfType(node, "JSXAttribute")) return false;
44347
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return false;
44348
+ const attributeName = node.name.name;
44349
+ return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
44350
+ };
44351
+ const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
44352
+ allowGlobalReactNamespace: true,
44353
+ resolveNamedAliases: true
44354
+ });
44355
+ const isReactLazyCall = (call, scopes) => isReactApiCall(call, "lazy", scopes, {
44356
+ allowGlobalReactNamespace: true,
44357
+ resolveNamedAliases: true
44358
+ });
44359
+ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
43755
44360
  let valueNode = identifier;
43756
44361
  let parent = valueNode.parent;
43757
44362
  while (parent) {
@@ -43761,20 +44366,26 @@ const isRenderFlowingReadReference = (identifier) => {
43761
44366
  continue;
43762
44367
  }
43763
44368
  switch (parent.type) {
43764
- case "JSXExpressionContainer":
43765
- case "ReturnStatement": return true;
43766
- case "ArrowFunctionExpression": return parent.body === valueNode;
44369
+ case "JSXOpeningElement": return parent.name === valueNode;
44370
+ case "JSXExpressionContainer": return Boolean(parent.parent && isElementTypeJsxAttribute(parent.parent));
44371
+ case "ReturnStatement": return false;
44372
+ case "ArrowFunctionExpression": return false;
43767
44373
  case "CallExpression":
43768
44374
  if (parent.callee === valueNode) return false;
43769
- if (isCreateElementCall(parent) || isCloneElementCall(parent)) return true;
44375
+ if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
44376
+ if (isReactCreateElementCall(parent, scopes)) return true;
43770
44377
  valueNode = parent;
43771
44378
  parent = parent.parent;
43772
44379
  continue;
43773
- case "VariableDeclarator": return parent.init === valueNode && isNodeOfType(parent.id, "Identifier") && isReactComponentName(parent.id.name);
43774
- case "AssignmentExpression": {
43775
- const assignmentTarget = parent.left;
43776
- return parent.right === valueNode && isNodeOfType(assignmentTarget, "Identifier") && isReactComponentName(assignmentTarget.name);
44380
+ case "VariableDeclarator": {
44381
+ if (parent.init !== valueNode || !isNodeOfType(parent.id, "Identifier")) return false;
44382
+ const aliasSymbol = scopes.symbolFor(parent.id);
44383
+ if (!aliasSymbol || aliasSymbol.kind !== "const" || visitedSymbols.has(aliasSymbol.id)) return false;
44384
+ const nextVisitedSymbols = new Set(visitedSymbols);
44385
+ nextVisitedSymbols.add(aliasSymbol.id);
44386
+ return aliasSymbol.references.some((reference) => reference.flag === "read" && isRenderFlowingReadReference(reference.identifier, scopes, nextVisitedSymbols));
43777
44387
  }
44388
+ case "AssignmentExpression": return false;
43778
44389
  case "Property":
43779
44390
  if (parent.value !== valueNode) return false;
43780
44391
  valueNode = parent;
@@ -43812,6 +44423,7 @@ const noUnstableNestedComponents = defineRule({
43812
44423
  severity: "warn",
43813
44424
  recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
43814
44425
  category: "Performance",
44426
+ tags: ["react-jsx-only"],
43815
44427
  create: (context) => {
43816
44428
  const settings = resolveSettings$8(context.settings);
43817
44429
  const renderPropRegex = compileGlob(settings.propNamePattern);
@@ -43874,7 +44486,7 @@ const noUnstableNestedComponents = defineRule({
43874
44486
  },
43875
44487
  Identifier(node) {
43876
44488
  if (!isReactComponentName(node.name)) return;
43877
- if (!isRenderFlowingReadReference(node)) return;
44489
+ if (!isRenderFlowingReadReference(node, context.scopes)) return;
43878
44490
  recordInstantiation(node, node.name);
43879
44491
  },
43880
44492
  FunctionDeclaration: checkFunctionLike,
@@ -43883,28 +44495,33 @@ const noUnstableNestedComponents = defineRule({
43883
44495
  ClassDeclaration(node) {
43884
44496
  if (!node.id) return;
43885
44497
  if (!isReactComponentName(node.id.name)) return;
43886
- if (!isReactClassComponent(node)) return;
44498
+ if (!isReactClassComponent(node, context.scopes)) return;
43887
44499
  enqueueCandidate(node, null);
43888
44500
  },
43889
44501
  ClassExpression(node) {
43890
44502
  const inferredName = node.id?.name ?? inferFunctionLikeName(node);
43891
44503
  if (!inferredName || !isReactComponentName(inferredName)) return;
43892
- if (!isReactClassComponent(node)) return;
44504
+ if (!isReactClassComponent(node, context.scopes)) return;
43893
44505
  enqueueCandidate(node, null);
43894
44506
  },
43895
44507
  CallExpression(node) {
43896
- if (isCreateElementCall(node)) {
44508
+ if (isReactCreateElementCall(node, context.scopes)) {
43897
44509
  const firstArgument = node.arguments[0];
43898
44510
  if (firstArgument && isNodeOfType(firstArgument, "Identifier")) recordInstantiation(firstArgument, firstArgument.name);
43899
44511
  else if (firstArgument && isNodeOfType(firstArgument, "MemberExpression")) recordMemberChainInstantiation(firstArgument);
43900
44512
  }
43901
- if (!isHocCallee$1(node)) return;
43902
- if (!hocCallContainsComponent(node)) return;
43903
- enqueueCandidate(node, null);
44513
+ const isReactLazy = isReactLazyCall(node, context.scopes);
44514
+ if (!isReactLazy && !isHocCallee$1(node)) return;
44515
+ if (!isReactLazy && !hocCallContainsComponent(node, context.scopes)) return;
44516
+ const inferredName = inferFunctionLikeName(node);
44517
+ const propInfo = isComponentDeclaredInProp(node);
44518
+ if (propInfo === null && (!inferredName || !isReactComponentName(inferredName))) return;
44519
+ enqueueCandidate(node, propInfo === null ? inferredName : null);
43904
44520
  },
43905
44521
  "Program:exit"() {
43906
44522
  for (const report of queuedReports) {
43907
44523
  if (report.requiredInstantiationName !== null) {
44524
+ if ((report.requiredInstantiationBinding ? context.scopes.symbolFor(report.requiredInstantiationBinding) : null)?.references.some((reference) => reference.flag === "write" || reference.flag === "read-write")) continue;
43908
44525
  if (!(report.requiredInstantiationBinding !== null ? instantiatedBindingIdentifiers.has(report.requiredInstantiationBinding) : instantiatedComponentNames.has(report.requiredInstantiationName))) continue;
43909
44526
  }
43910
44527
  context.report({
@@ -44078,7 +44695,7 @@ const noWideLetterSpacing = defineRule({
44078
44695
  //#endregion
44079
44696
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
44080
44697
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
44081
- const MESSAGE$9 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
44698
+ const MESSAGE$10 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
44082
44699
  const resolveSettings$7 = (settings) => {
44083
44700
  const reactDoctor = settings?.["react-doctor"];
44084
44701
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -44112,7 +44729,7 @@ const noWillUpdateSetState = defineRule({
44112
44729
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
44113
44730
  context.report({
44114
44731
  node: node.callee,
44115
- message: MESSAGE$9
44732
+ message: MESSAGE$10
44116
44733
  });
44117
44734
  } };
44118
44735
  }
@@ -44937,7 +45554,7 @@ const findChildrenDestructuringFunction = (identifier, scopes) => {
44937
45554
  const symbol = scopes.symbolFor(identifier);
44938
45555
  if (symbol) {
44939
45556
  if (symbol.kind !== "parameter") return null;
44940
- const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45557
+ const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
44941
45558
  if (declaringFunction && destructuresChildrenAsFirstParam(declaringFunction)) return declaringFunction;
44942
45559
  return null;
44943
45560
  }
@@ -44983,7 +45600,7 @@ const isChildrenMemberExpression = (node, scopes) => {
44983
45600
  const symbol = scopes.symbolFor(propsObject);
44984
45601
  if (symbol) {
44985
45602
  if (symbol.kind !== "parameter") return false;
44986
- const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45603
+ const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
44987
45604
  return declaringFunction ? isDeclaringFunctionComponentLike(declaringFunction) : false;
44988
45605
  }
44989
45606
  return hasComponentLikeAncestorFunction(node);
@@ -45108,7 +45725,7 @@ const preactNoRenderArguments = defineRule({
45108
45725
  });
45109
45726
  //#endregion
45110
45727
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
45111
- 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.";
45728
+ 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.";
45112
45729
  const preactPreferOndblclick = defineRule({
45113
45730
  id: "preact-prefer-ondblclick",
45114
45731
  title: "onDoubleClick instead of onDblClick",
@@ -45123,7 +45740,7 @@ const preactPreferOndblclick = defineRule({
45123
45740
  if (!onDoubleClickAttribute) return;
45124
45741
  context.report({
45125
45742
  node: onDoubleClickAttribute,
45126
- message: MESSAGE$8
45743
+ message: MESSAGE$9
45127
45744
  });
45128
45745
  } })
45129
45746
  });
@@ -45374,7 +45991,7 @@ const preferExplicitVariants = defineRule({
45374
45991
  });
45375
45992
  //#endregion
45376
45993
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
45377
- 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.";
45994
+ 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.";
45378
45995
  const resolveSettings$4 = (settings) => {
45379
45996
  const reactDoctor = settings?.["react-doctor"];
45380
45997
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -45413,7 +46030,7 @@ const preferFunctionComponent = defineRule({
45413
46030
  const reportNode = node.id ?? node;
45414
46031
  context.report({
45415
46032
  node: reportNode,
45416
- message: MESSAGE$7
46033
+ message: MESSAGE$8
45417
46034
  });
45418
46035
  };
45419
46036
  return {
@@ -45635,7 +46252,7 @@ const doesFunctionReturnsObjectLiteral = (functionNode) => {
45635
46252
  //#endregion
45636
46253
  //#region src/plugin/utils/enclosing-component-or-hook-scope.ts
45637
46254
  const enclosingComponentOrHookScope = (startNode, ownScopeFor) => {
45638
- const functionNode = findEnclosingFunction(startNode);
46255
+ const functionNode = findEnclosingFunction$1(startNode);
45639
46256
  if (!functionNode) return null;
45640
46257
  const displayName = componentOrHookDisplayNameForFunction(functionNode);
45641
46258
  if (!displayName) return null;
@@ -46697,7 +47314,7 @@ const isTanstackQuerySource = (source) => TANSTACK_QUERY_PACKAGE_PATTERN.test(so
46697
47314
  //#region src/plugin/rules/tanstack-query/query-destructure-result.ts
46698
47315
  const isHookReturnForwardingSpread = (objectExpression) => {
46699
47316
  const objectParent = objectExpression.parent;
46700
- const enclosingFunction = findEnclosingFunction(objectExpression);
47317
+ const enclosingFunction = findEnclosingFunction$1(objectExpression);
46701
47318
  if (!enclosingFunction) return false;
46702
47319
  if (!(isNodeOfType(objectParent, "ReturnStatement") || isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && enclosingFunction.body === objectExpression)) return false;
46703
47320
  const enclosingName = componentOrHookDisplayNameForFunction(enclosingFunction);
@@ -46991,7 +47608,258 @@ const queryMutationMissingInvalidation = defineRule({
46991
47608
  }
46992
47609
  });
46993
47610
  //#endregion
47611
+ //#region src/plugin/utils/is-node-conditionally-executed.ts
47612
+ const isNodeConditionallyExecuted = (node, boundary) => {
47613
+ let child = node;
47614
+ let parent = child.parent ?? null;
47615
+ while (parent && parent !== boundary) {
47616
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === child || parent.alternate === child)) return true;
47617
+ if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) return true;
47618
+ if (isNodeOfType(parent, "AssignmentPattern") && parent.right === child) return true;
47619
+ child = parent;
47620
+ parent = child.parent ?? null;
47621
+ }
47622
+ return false;
47623
+ };
47624
+ //#endregion
47625
+ //#region src/plugin/rules/tanstack-query/utils/resolve-tanstack-query-hook-name.ts
47626
+ const resolveTanstackNamespaceHookName = (memberExpression, contextNode, scopes) => {
47627
+ const hookName = getStaticPropertyKeyName(memberExpression, { allowComputedString: true });
47628
+ const namespaceObject = stripParenExpression(memberExpression.object);
47629
+ if (!hookName || !TANSTACK_QUERY_HOOKS.has(hookName)) return null;
47630
+ if (!isNodeOfType(namespaceObject, "Identifier")) return null;
47631
+ const resolvedNamespaceSymbol = resolveConstIdentifierAlias(namespaceObject, scopes);
47632
+ if (resolvedNamespaceSymbol?.kind !== "import") return null;
47633
+ const namespaceBinding = getImportBindingForName(contextNode, resolvedNamespaceSymbol.name);
47634
+ return namespaceBinding?.isNamespace && isTanstackQuerySource(namespaceBinding.source) ? hookName : null;
47635
+ };
47636
+ const resolveTanstackQueryHookName = (callExpression, scopes) => {
47637
+ const callee = stripParenExpression(callExpression.callee);
47638
+ if (isNodeOfType(callee, "Identifier")) {
47639
+ const resolvedSymbol = resolveConstIdentifierAlias(callee, scopes);
47640
+ if (!resolvedSymbol) return null;
47641
+ if (resolvedSymbol.kind === "const" && resolvedSymbol.initializer) {
47642
+ const initializer = stripParenExpression(resolvedSymbol.initializer);
47643
+ return isNodeOfType(initializer, "MemberExpression") ? resolveTanstackNamespaceHookName(initializer, callExpression, scopes) : null;
47644
+ }
47645
+ if (resolvedSymbol.kind !== "import") return null;
47646
+ const importBinding = getImportBindingForName(callExpression, resolvedSymbol.name);
47647
+ if (importBinding === null) return null;
47648
+ if (importBinding.isNamespace || !isTanstackQuerySource(importBinding.source)) return null;
47649
+ return importBinding.exportedName !== null && TANSTACK_QUERY_HOOKS.has(importBinding.exportedName) ? importBinding.exportedName : null;
47650
+ }
47651
+ if (isNodeOfType(callee, "MemberExpression")) return resolveTanstackNamespaceHookName(callee, callExpression, scopes);
47652
+ return null;
47653
+ };
47654
+ const resolveTanstackQueryHookNameFromInitializer = (initializer, scopes) => {
47655
+ const unwrappedInitializer = stripParenExpression(initializer);
47656
+ if (isNodeOfType(unwrappedInitializer, "CallExpression")) return resolveTanstackQueryHookName(unwrappedInitializer, scopes);
47657
+ if (!isNodeOfType(unwrappedInitializer, "Identifier")) return null;
47658
+ const resolvedSymbol = resolveConstIdentifierAlias(unwrappedInitializer, scopes);
47659
+ if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
47660
+ const resolvedInitializer = stripParenExpression(resolvedSymbol.initializer);
47661
+ if (!isNodeOfType(resolvedInitializer, "CallExpression")) return null;
47662
+ return resolveTanstackQueryHookName(resolvedInitializer, scopes);
47663
+ };
47664
+ //#endregion
46994
47665
  //#region src/plugin/rules/tanstack-query/query-no-query-in-effect.ts
47666
+ const isTanstackQueryResult = (expression, context) => Boolean(resolveTanstackQueryHookNameFromInitializer(expression, context.scopes));
47667
+ const isStaticRefetchMember = (memberExpression) => getStaticPropertyKeyName(memberExpression, { allowComputedString: true }) === "refetch";
47668
+ const resolveCalledFunction = (callee, context) => {
47669
+ const unwrappedCallee = stripParenExpression(callee);
47670
+ if (isFunctionLike$2(unwrappedCallee)) return unwrappedCallee;
47671
+ if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
47672
+ const symbol = resolveConstIdentifierAlias(unwrappedCallee, context.scopes);
47673
+ if (!symbol) return null;
47674
+ const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
47675
+ return candidate && isFunctionLike$2(candidate) ? candidate : null;
47676
+ };
47677
+ const hasSuspensionBefore = (functionNode, boundary, context) => {
47678
+ if (!isFunctionLike$2(functionNode)) return true;
47679
+ if (functionNode.generator) return true;
47680
+ const boundaryStart = getRangeStart(boundary);
47681
+ if (boundaryStart === null) return true;
47682
+ let hasSuspension = false;
47683
+ walkAst(functionNode, (node) => {
47684
+ if (node !== functionNode && isFunctionLike$2(node)) return false;
47685
+ if (!isNodeOfType(node, "AwaitExpression") || !isNodeReachableWithinFunction(node, context)) return;
47686
+ const suspensionStart = getRangeStart(node);
47687
+ if (suspensionStart !== null && suspensionStart < boundaryStart) {
47688
+ hasSuspension = true;
47689
+ return false;
47690
+ }
47691
+ });
47692
+ return hasSuspension;
47693
+ };
47694
+ const isFunctionAncestor = (ancestor, functionNode) => {
47695
+ let enclosingFunction = findEnclosingFunction$1(functionNode);
47696
+ while (enclosingFunction) {
47697
+ if (enclosingFunction === ancestor) return true;
47698
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47699
+ }
47700
+ return false;
47701
+ };
47702
+ 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 === "??="));
47703
+ const functionInvokesTarget = (callerFunction, targetFunction, context, visitedFunctions, canCrossSuspension = false) => {
47704
+ if (visitedFunctions.has(callerFunction)) return false;
47705
+ visitedFunctions.add(callerFunction);
47706
+ let invokesTarget = false;
47707
+ walkAst(callerFunction, (node) => {
47708
+ if (node !== callerFunction && isFunctionLike$2(node)) return false;
47709
+ if (!isNodeOfType(node, "CallExpression")) return;
47710
+ if (!isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context) || !canCrossSuspension && hasSuspensionBefore(callerFunction, node, context)) return;
47711
+ const calledFunction = resolveCalledFunction(node.callee, context);
47712
+ if (calledFunction === targetFunction || calledFunction && functionInvokesTarget(calledFunction, targetFunction, context, visitedFunctions, canCrossSuspension)) {
47713
+ invokesTarget = true;
47714
+ return false;
47715
+ }
47716
+ });
47717
+ return invokesTarget;
47718
+ };
47719
+ const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
47720
+ const boundaryFunction = findEnclosingFunction$1(boundary);
47721
+ const boundaryStart = getRangeStart(boundary);
47722
+ if (!boundaryFunction || boundaryStart === null) return false;
47723
+ let isInvokedBefore = false;
47724
+ walkAst(boundaryFunction, (node) => {
47725
+ if (node !== boundaryFunction && isFunctionLike$2(node)) return false;
47726
+ if (!isNodeOfType(node, "CallExpression")) return;
47727
+ const callStart = getRangeStart(node);
47728
+ if (callStart === null || callStart >= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, boundaryFunction, context) || hasSuspensionBefore(boundaryFunction, node, context)) return;
47729
+ const calledFunction = resolveCalledFunction(node.callee, context);
47730
+ if (calledFunction === invokedFunction || calledFunction && functionInvokesTarget(calledFunction, invokedFunction, context, /* @__PURE__ */ new Set())) {
47731
+ isInvokedBefore = true;
47732
+ return false;
47733
+ }
47734
+ });
47735
+ return isInvokedBefore;
47736
+ };
47737
+ const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, context) => {
47738
+ const boundaryStart = getRangeStart(boundary);
47739
+ if (boundaryStart === null) return false;
47740
+ let isInvokedAfter = false;
47741
+ walkAst(callerFunction, (node) => {
47742
+ if (node !== callerFunction && isFunctionLike$2(node)) return false;
47743
+ if (!isNodeOfType(node, "CallExpression")) return;
47744
+ const callStart = getRangeStart(node);
47745
+ if (callStart === null || callStart <= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context)) return;
47746
+ const calledFunction = resolveCalledFunction(node.callee, context);
47747
+ if (calledFunction === invokedFunction || calledFunction && functionInvokesTarget(calledFunction, invokedFunction, context, /* @__PURE__ */ new Set(), true)) {
47748
+ isInvokedAfter = true;
47749
+ return false;
47750
+ }
47751
+ });
47752
+ return isInvokedAfter;
47753
+ };
47754
+ const isWriteExecutedBefore = (writeNode, boundary, context, deferredExecutionFunction) => {
47755
+ const writeStart = getRangeStart(writeNode);
47756
+ const boundaryStart = getRangeStart(boundary);
47757
+ const writeFunction = findEnclosingFunction$1(writeNode);
47758
+ const writeExecutionBoundary = writeFunction ?? findProgramRoot(writeNode);
47759
+ if (writeStart === null || boundaryStart === null || !writeExecutionBoundary || !isNodeReachableWithinFunction(writeNode, context) || !isUnconditionallyExecuted(writeNode, writeExecutionBoundary, context)) return false;
47760
+ const boundaryFunction = findEnclosingFunction$1(boundary);
47761
+ const renderFunction = deferredExecutionFunction ? findEnclosingFunction$1(deferredExecutionFunction) : null;
47762
+ if (writeFunction === boundaryFunction) return writeStart < boundaryStart;
47763
+ if (!writeFunction) return writeStart < boundaryStart;
47764
+ if (boundaryFunction && isFunctionAncestor(writeFunction, boundaryFunction)) {
47765
+ if (Boolean(renderFunction && (writeFunction === renderFunction || isFunctionAncestor(writeFunction, renderFunction)))) return true;
47766
+ if (deferredExecutionFunction && boundaryFunction !== deferredExecutionFunction) {
47767
+ if (hasSuspensionBefore(boundaryFunction, boundary, context) && isFunctionInvokedBefore(boundaryFunction, writeNode, context)) return true;
47768
+ return isFunctionInvokedAfter(boundaryFunction, writeNode, writeFunction, context);
47769
+ }
47770
+ return writeStart < boundaryStart;
47771
+ }
47772
+ if (renderFunction && isFunctionAncestor(renderFunction, writeFunction) && !hasSuspensionBefore(writeFunction, writeNode, context) && functionInvokesTarget(renderFunction, writeFunction, context, /* @__PURE__ */ new Set())) return true;
47773
+ return !hasSuspensionBefore(writeFunction, writeNode, context) && isFunctionInvokedBefore(writeFunction, boundary, context);
47774
+ };
47775
+ const getStaticStringValue = (node) => {
47776
+ const unwrappedNode = stripParenExpression(node);
47777
+ if (isNodeOfType(unwrappedNode, "Literal") && typeof unwrappedNode.value === "string") return unwrappedNode.value;
47778
+ if (isNodeOfType(unwrappedNode, "TemplateLiteral") && unwrappedNode.expressions.length === 0) return getStaticTemplateLiteralValue(unwrappedNode);
47779
+ return null;
47780
+ };
47781
+ const isSameRefetchMember = (target, candidate, context) => {
47782
+ const unwrappedTarget = stripParenExpression(target);
47783
+ const unwrappedCandidate = stripParenExpression(candidate);
47784
+ if (!isNodeOfType(unwrappedTarget, "Identifier") || !isNodeOfType(unwrappedCandidate, "MemberExpression") || !isStaticRefetchMember(unwrappedCandidate)) return false;
47785
+ const candidateTarget = stripParenExpression(unwrappedCandidate.object);
47786
+ if (!isNodeOfType(candidateTarget, "Identifier")) return false;
47787
+ const targetSymbol = resolveConstIdentifierAlias(unwrappedTarget, context.scopes);
47788
+ const candidateSymbol = resolveConstIdentifierAlias(candidateTarget, context.scopes);
47789
+ return Boolean(targetSymbol && candidateSymbol?.id === targetSymbol.id);
47790
+ };
47791
+ const getRefetchMutationTarget = (node, context) => {
47792
+ if (isNodeOfType(node, "MemberExpression") && isStaticRefetchMember(node)) {
47793
+ const parent = node.parent;
47794
+ if (isNodeOfType(parent, "AssignmentExpression") && parent.left === node && isSameRefetchMember(node.object, parent.right, context)) return null;
47795
+ return isNodeOfType(parent, "AssignmentExpression") && parent.left === node || isNodeOfType(parent, "UpdateExpression") && parent.argument === node || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" ? node.object : null;
47796
+ }
47797
+ if (!isNodeOfType(node, "CallExpression")) return null;
47798
+ const callee = stripParenExpression(node.callee);
47799
+ if (!isNodeOfType(callee, "MemberExpression")) return null;
47800
+ const receiver = stripParenExpression(callee.object);
47801
+ if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || context.scopes.symbolFor(receiver)) return null;
47802
+ const methodName = getStaticPropertyKeyName(callee, { allowComputedString: true });
47803
+ const target = node.arguments[0];
47804
+ if (!target || isNodeOfType(target, "SpreadElement")) return null;
47805
+ if (methodName === "defineProperty") {
47806
+ const propertyKey = node.arguments[1];
47807
+ if (!propertyKey || getStaticStringValue(propertyKey) !== "refetch") return null;
47808
+ const descriptor = node.arguments[2];
47809
+ if (isNodeOfType(descriptor, "ObjectExpression")) {
47810
+ const valueProperty = descriptor.properties.find((property) => isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "value");
47811
+ if (isNodeOfType(valueProperty, "Property") && isSameRefetchMember(target, valueProperty.value, context)) return null;
47812
+ }
47813
+ return target;
47814
+ }
47815
+ if (methodName !== "assign") return null;
47816
+ let finalRefetchValue = null;
47817
+ for (const source of node.arguments.slice(1)) {
47818
+ if (!isNodeOfType(source, "ObjectExpression")) continue;
47819
+ for (const property of source.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "refetch") finalRefetchValue = property.value;
47820
+ }
47821
+ if (!finalRefetchValue || isSameRefetchMember(target, finalRefetchValue, context)) return null;
47822
+ return target;
47823
+ };
47824
+ const hasRefetchMemberWriteBefore = (expression, boundary, context, deferredExecutionFunction) => {
47825
+ const unwrappedExpression = stripParenExpression(expression);
47826
+ if (!isNodeOfType(unwrappedExpression, "Identifier")) return false;
47827
+ const resultSymbol = resolveConstIdentifierAlias(unwrappedExpression, context.scopes);
47828
+ if (!resultSymbol) return false;
47829
+ const program = findProgramRoot(expression);
47830
+ if (!program) return true;
47831
+ if (getRangeStart(boundary) === null) return true;
47832
+ let hasWrite = false;
47833
+ walkAst(program, (node) => {
47834
+ const mutationTarget = getRefetchMutationTarget(node, context);
47835
+ if (!mutationTarget) return;
47836
+ if (!isWriteExecutedBefore(node, boundary, context, deferredExecutionFunction)) return;
47837
+ const object = stripParenExpression(mutationTarget);
47838
+ if (!isNodeOfType(object, "Identifier")) return;
47839
+ if (resolveConstIdentifierAlias(object, context.scopes)?.id === resultSymbol.id) {
47840
+ hasWrite = true;
47841
+ return false;
47842
+ }
47843
+ });
47844
+ return hasWrite;
47845
+ };
47846
+ const isTanstackRefetchExpression = (expression, context, deferredExecutionFunction, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
47847
+ const unwrappedExpression = stripParenExpression(expression);
47848
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) return isStaticRefetchMember(unwrappedExpression) && isTanstackQueryResult(unwrappedExpression.object, context) && !hasRefetchMemberWriteBefore(unwrappedExpression.object, unwrappedExpression, context, deferredExecutionFunction);
47849
+ if (!isNodeOfType(unwrappedExpression, "Identifier")) return false;
47850
+ const symbol = context.scopes.symbolFor(unwrappedExpression);
47851
+ if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read") || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
47852
+ visitedSymbolIds.add(symbol.id);
47853
+ const bindingProperty = symbol.bindingIdentifier.parent;
47854
+ if (isNodeOfType(bindingProperty, "Property") && getStaticPropertyKeyName(bindingProperty, { allowComputedString: true }) === "refetch") {
47855
+ const initializer = symbol.declarationNode.init;
47856
+ return Boolean(initializer && isTanstackQueryResult(initializer, context) && !hasRefetchMemberWriteBefore(initializer, symbol.declarationNode, context, null));
47857
+ }
47858
+ return Boolean(symbol.declarationNode.id === symbol.bindingIdentifier && symbol.initializer && isTanstackRefetchExpression(symbol.initializer, context, null, visitedSymbolIds));
47859
+ };
47860
+ const isTanstackRefetchCall = (callExpression, context, effectCallback) => {
47861
+ return isTanstackRefetchExpression(callExpression.callee, context, effectCallback);
47862
+ };
46995
47863
  const queryNoQueryInEffect = defineRule({
46996
47864
  id: "query-no-query-in-effect",
46997
47865
  title: "Query refetch inside useEffect",
@@ -47007,8 +47875,7 @@ const queryNoQueryInEffect = defineRule({
47007
47875
  walkAst(callback, (child) => {
47008
47876
  if (child !== callback && isFunctionLike$2(child) && !effectInvokedFunctions.has(child)) return false;
47009
47877
  if (!isNodeOfType(child, "CallExpression")) return;
47010
- const callee = child.callee;
47011
- if (isNodeOfType(callee, "Identifier") && callee.name === "refetch" || isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === "refetch") context.report({
47878
+ if (isTanstackRefetchCall(child, context, callback)) context.report({
47012
47879
  node: child,
47013
47880
  message: "refetch() inside useEffect duplicates work React Query already does, causing extra fetches."
47014
47881
  });
@@ -47017,28 +47884,6 @@ const queryNoQueryInEffect = defineRule({
47017
47884
  });
47018
47885
  //#endregion
47019
47886
  //#region src/plugin/rules/tanstack-query/query-no-rest-destructuring.ts
47020
- const resolveTanstackQueryHookName = (callExpression) => {
47021
- const callee = callExpression.callee;
47022
- if (isNodeOfType(callee, "Identifier")) {
47023
- const importBinding = getImportBindingForName(callExpression, callee.name);
47024
- if (importBinding === null) return TANSTACK_QUERY_HOOKS.has(callee.name) ? callee.name : null;
47025
- if (importBinding.isNamespace || !isTanstackQuerySource(importBinding.source)) return null;
47026
- return importBinding.exportedName !== null && TANSTACK_QUERY_HOOKS.has(importBinding.exportedName) ? importBinding.exportedName : null;
47027
- }
47028
- if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && TANSTACK_QUERY_HOOKS.has(callee.property.name) && isNodeOfType(callee.object, "Identifier")) {
47029
- const namespaceBinding = getImportBindingForName(callExpression, callee.object.name);
47030
- if (namespaceBinding?.isNamespace && isTanstackQuerySource(namespaceBinding.source)) return callee.property.name;
47031
- }
47032
- return null;
47033
- };
47034
- const resolveHookNameFromInitializer = (initializer, scopes) => {
47035
- if (isNodeOfType(initializer, "CallExpression")) return resolveTanstackQueryHookName(initializer);
47036
- if (!isNodeOfType(initializer, "Identifier")) return null;
47037
- const resolvedSymbol = resolveConstIdentifierAlias(initializer, scopes);
47038
- if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
47039
- if (!isNodeOfType(resolvedSymbol.initializer, "CallExpression")) return null;
47040
- return resolveTanstackQueryHookName(resolvedSymbol.initializer);
47041
- };
47042
47887
  const queryNoRestDestructuring = defineRule({
47043
47888
  id: "query-no-rest-destructuring",
47044
47889
  title: "Rest destructuring on query result",
@@ -47050,7 +47895,7 @@ const queryNoRestDestructuring = defineRule({
47050
47895
  if (!isNodeOfType(node.id, "ObjectPattern")) return;
47051
47896
  if (!node.init) return;
47052
47897
  if (!node.id.properties?.some((property) => isNodeOfType(property, "RestElement"))) return;
47053
- const hookName = resolveHookNameFromInitializer(node.init, context.scopes);
47898
+ const hookName = resolveTanstackQueryHookNameFromInitializer(node.init, context.scopes);
47054
47899
  if (!hookName) return;
47055
47900
  context.report({
47056
47901
  node: node.id,
@@ -47185,8 +48030,8 @@ const queryStableQueryClient = defineRule({
47185
48030
  create: (context) => ({ NewExpression(node) {
47186
48031
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "QueryClient") return;
47187
48032
  if (isStableHookWrapperArgument(node)) return;
47188
- let enclosingFunction = findEnclosingFunction(node);
47189
- while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
48033
+ let enclosingFunction = findEnclosingFunction$1(node);
48034
+ while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47190
48035
  if (!enclosingFunction) return;
47191
48036
  if (!isComponentFunction(enclosingFunction)) return;
47192
48037
  context.report({
@@ -47281,7 +48126,7 @@ const reactCompilerNoManualMemoization = defineRule({
47281
48126
  const comparatorArgument = node.arguments?.[1];
47282
48127
  if (comparatorArgument && !isNullishComparatorArgument(comparatorArgument)) return;
47283
48128
  } else {
47284
- const enclosingFunction = findEnclosingFunction(node);
48129
+ const enclosingFunction = findEnclosingFunction$1(node);
47285
48130
  if (!enclosingFunction || !isCompilerInferableFunction(enclosingFunction)) return;
47286
48131
  }
47287
48132
  const removalMessage = REMOVAL_MESSAGE_BY_REACT_API_NAME.get(apiName);
@@ -47294,7 +48139,7 @@ const reactCompilerNoManualMemoization = defineRule({
47294
48139
  });
47295
48140
  //#endregion
47296
48141
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
47297
- const MESSAGE$6 = "This JSX crashes because `React` isn't in scope.";
48142
+ const MESSAGE$7 = "This JSX crashes because `React` isn't in scope.";
47298
48143
  const reactInJsxScope = defineRule({
47299
48144
  id: "react-in-jsx-scope",
47300
48145
  title: "React not in scope for JSX",
@@ -47306,19 +48151,190 @@ const reactInJsxScope = defineRule({
47306
48151
  if (findVariableInitializer(node, "React")) return;
47307
48152
  context.report({
47308
48153
  node: node.name,
47309
- message: MESSAGE$6
48154
+ message: MESSAGE$7
47310
48155
  });
47311
48156
  },
47312
48157
  JSXFragment(node) {
47313
48158
  if (findVariableInitializer(node, "React")) return;
47314
48159
  context.report({
47315
48160
  node: node.openingFragment,
47316
- message: MESSAGE$6
48161
+ message: MESSAGE$7
47317
48162
  });
47318
48163
  }
47319
48164
  })
47320
48165
  });
47321
48166
  //#endregion
48167
+ //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
48168
+ 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.";
48169
+ const REACT_MARKDOWN_MODULE = "react-markdown";
48170
+ const REHYPE_RAW_MODULE = "rehype-raw";
48171
+ const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
48172
+ const DOMPURIFY_MODULES = new Set(["dompurify", "isomorphic-dompurify"]);
48173
+ const REACT_MARKDOWN_NAMED_EXPORTS = new Set(["MarkdownAsync", "MarkdownHooks"]);
48174
+ const REACT_MARKDOWN_NAMESPACE_EXPORTS = new Set(["default", ...REACT_MARKDOWN_NAMED_EXPORTS]);
48175
+ const DEFAULT_EXPORT_NAMES = new Set(["default"]);
48176
+ const getImportDeclaration = (symbol) => {
48177
+ if (symbol.kind !== "import") return null;
48178
+ const importDeclaration = symbol.declarationNode.parent;
48179
+ return isNodeOfType(importDeclaration, "ImportDeclaration") ? importDeclaration : null;
48180
+ };
48181
+ const isImportFromModule = (symbol, moduleName) => getImportDeclaration(symbol)?.source.value === moduleName;
48182
+ const isDefaultImportSymbol = (symbol, moduleName) => {
48183
+ if (!isImportFromModule(symbol, moduleName)) return false;
48184
+ return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "default";
48185
+ };
48186
+ const resolveImportedIdentifier = (node, scopes) => {
48187
+ if (!isNodeOfType(node, "Identifier") && !isNodeOfType(node, "JSXIdentifier")) return null;
48188
+ const symbol = resolveConstIdentifierAlias(node, scopes);
48189
+ return symbol?.kind === "import" ? symbol : null;
48190
+ };
48191
+ const getStaticMemberParts = (node) => {
48192
+ if (isNodeOfType(node, "MemberExpression")) {
48193
+ if (node.computed || !isNodeOfType(node.property, "Identifier")) return null;
48194
+ return {
48195
+ object: node.object,
48196
+ propertyName: node.property.name
48197
+ };
48198
+ }
48199
+ if (isNodeOfType(node, "JSXMemberExpression")) {
48200
+ if (!isNodeOfType(node.property, "JSXIdentifier")) return null;
48201
+ return {
48202
+ object: node.object,
48203
+ propertyName: node.property.name
48204
+ };
48205
+ }
48206
+ return null;
48207
+ };
48208
+ const isNamespaceMemberFromModule = (node, moduleName, memberNames, scopes) => {
48209
+ const memberParts = getStaticMemberParts(node);
48210
+ if (!memberParts || !memberNames.has(memberParts.propertyName)) return false;
48211
+ const namespaceSymbol = resolveImportedIdentifier(memberParts.object, scopes);
48212
+ return Boolean(namespaceSymbol && isImportFromModule(namespaceSymbol, moduleName) && isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier"));
48213
+ };
48214
+ const isReactMarkdownComponent = (node, scopes) => {
48215
+ if (isNodeOfType(node, "JSXIdentifier")) {
48216
+ const symbol = resolveImportedIdentifier(node, scopes);
48217
+ if (!symbol || !isImportFromModule(symbol, REACT_MARKDOWN_MODULE)) return false;
48218
+ if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return true;
48219
+ const importedName = getImportedName(symbol.declarationNode);
48220
+ return importedName === "default" || Boolean(importedName && REACT_MARKDOWN_NAMED_EXPORTS.has(importedName));
48221
+ }
48222
+ return isNamespaceMemberFromModule(node, REACT_MARKDOWN_MODULE, REACT_MARKDOWN_NAMESPACE_EXPORTS, scopes);
48223
+ };
48224
+ const isPluginFromModule = (rawNode, moduleName, scopes, visitedSymbolIds) => {
48225
+ const node = stripParenExpression(rawNode);
48226
+ if (isNodeOfType(node, "Identifier")) {
48227
+ const symbol = resolveConstIdentifierAlias(node, scopes);
48228
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
48229
+ if (isDefaultImportSymbol(symbol, moduleName)) return true;
48230
+ if (symbol.kind !== "const" || !symbol.initializer) return false;
48231
+ visitedSymbolIds.add(symbol.id);
48232
+ return isPluginFromModule(symbol.initializer, moduleName, scopes, visitedSymbolIds);
48233
+ }
48234
+ if (isNamespaceMemberFromModule(node, moduleName, DEFAULT_EXPORT_NAMES, scopes)) return true;
48235
+ if (!isNodeOfType(node, "ArrayExpression")) return false;
48236
+ for (const element of node.elements) {
48237
+ if (!element || isNodeOfType(element, "SpreadElement")) continue;
48238
+ return isPluginFromModule(element, moduleName, scopes, visitedSymbolIds);
48239
+ }
48240
+ return false;
48241
+ };
48242
+ const collectPluginEntries = (rawNode, scopes, visitedSymbolIds) => {
48243
+ const node = stripParenExpression(rawNode);
48244
+ if (isNodeOfType(node, "Identifier")) {
48245
+ const symbol = scopes.referenceFor(node)?.resolvedSymbol;
48246
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
48247
+ visitedSymbolIds.add(symbol.id);
48248
+ return collectPluginEntries(symbol.initializer, scopes, visitedSymbolIds);
48249
+ }
48250
+ if (!isNodeOfType(node, "ArrayExpression")) return null;
48251
+ const entries = [];
48252
+ for (const element of node.elements) {
48253
+ if (!element) continue;
48254
+ if (isNodeOfType(element, "SpreadElement")) {
48255
+ const spreadEntries = collectPluginEntries(element.argument, scopes, new Set(visitedSymbolIds));
48256
+ if (spreadEntries === null) return null;
48257
+ entries.push(...spreadEntries);
48258
+ continue;
48259
+ }
48260
+ entries.push(element);
48261
+ }
48262
+ return entries;
48263
+ };
48264
+ const findEffectiveExplicitAttribute = (attributes, attributeName) => {
48265
+ for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
48266
+ const attribute = attributes[attributeIndex];
48267
+ if (!attribute || isNodeOfType(attribute, "JSXSpreadAttribute")) return null;
48268
+ if (isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && attribute.name.name === attributeName) return attribute;
48269
+ }
48270
+ return null;
48271
+ };
48272
+ const getAttributeExpression = (attribute) => {
48273
+ if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
48274
+ return isNodeOfType(attribute.value.expression, "JSXEmptyExpression") ? null : attribute.value.expression;
48275
+ };
48276
+ const isDomPurifyNamespace = (node, scopes) => {
48277
+ const symbol = resolveImportedIdentifier(node, scopes);
48278
+ if (!symbol) return false;
48279
+ const importDeclaration = getImportDeclaration(symbol);
48280
+ if (!importDeclaration || !DOMPURIFY_MODULES.has(String(importDeclaration.source.value))) return false;
48281
+ return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
48282
+ };
48283
+ const isDomPurifySanitizeCall = (node, scopes) => {
48284
+ if (!isNodeOfType(node, "CallExpression")) return false;
48285
+ const memberParts = getStaticMemberParts(stripParenExpression(node.callee));
48286
+ return Boolean(memberParts && memberParts.propertyName === "sanitize" && isDomPurifyNamespace(memberParts.object, scopes));
48287
+ };
48288
+ const isStaticOrSanitizedMarkdownExpression = (rawNode, scopes, visitedSymbolIds) => {
48289
+ const node = stripParenExpression(rawNode);
48290
+ if (isNodeOfType(node, "Literal")) return true;
48291
+ if (isNodeOfType(node, "TemplateLiteral")) return node.expressions.every((expression) => isStaticOrSanitizedMarkdownExpression(expression, scopes, new Set(visitedSymbolIds)));
48292
+ if (isDomPurifySanitizeCall(node, scopes)) return true;
48293
+ if (isNodeOfType(node, "ConditionalExpression")) return isStaticOrSanitizedMarkdownExpression(node.consequent, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.alternate, scopes, new Set(visitedSymbolIds));
48294
+ if (isNodeOfType(node, "BinaryExpression") && node.operator === "+") return isStaticOrSanitizedMarkdownExpression(node.left, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.right, scopes, new Set(visitedSymbolIds));
48295
+ if (!isNodeOfType(node, "Identifier")) return false;
48296
+ const symbol = scopes.referenceFor(node)?.resolvedSymbol;
48297
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
48298
+ visitedSymbolIds.add(symbol.id);
48299
+ return isStaticOrSanitizedMarkdownExpression(symbol.initializer, scopes, visitedSymbolIds);
48300
+ };
48301
+ const hasDynamicUnsanitizedChildren = (openingElement, scopes) => {
48302
+ const jsxElement = openingElement.parent;
48303
+ if (!isNodeOfType(jsxElement, "JSXElement")) return false;
48304
+ const meaningfulChildren = jsxElement.children.filter((child) => !isNodeOfType(child, "JSXText") || child.value.trim().length > 0);
48305
+ if (meaningfulChildren.length > 0) return meaningfulChildren.some((child) => {
48306
+ if (isNodeOfType(child, "JSXText")) return false;
48307
+ if (!isNodeOfType(child, "JSXExpressionContainer")) return true;
48308
+ if (isNodeOfType(child.expression, "JSXEmptyExpression")) return false;
48309
+ return !isStaticOrSanitizedMarkdownExpression(child.expression, scopes, /* @__PURE__ */ new Set());
48310
+ });
48311
+ const childrenAttribute = findEffectiveExplicitAttribute(openingElement.attributes, "children");
48312
+ if (!childrenAttribute) return false;
48313
+ const childrenExpression = getAttributeExpression(childrenAttribute);
48314
+ return Boolean(childrenExpression && !isStaticOrSanitizedMarkdownExpression(childrenExpression, scopes, /* @__PURE__ */ new Set()));
48315
+ };
48316
+ const reactMarkdownUnsanitizedRawHtml = defineRule({
48317
+ id: "react-markdown-unsanitized-raw-html",
48318
+ title: "Unsanitized raw HTML in React Markdown",
48319
+ severity: "warn",
48320
+ recommendation: "Add `rehype-sanitize` to `rehypePlugins` or sanitize dynamic markdown before rendering. `skipHtml` does not disable HTML already parsed by `rehype-raw`.",
48321
+ create: skipNonProductionFiles((context) => ({ JSXOpeningElement(node) {
48322
+ if (!isReactMarkdownComponent(node.name, context.scopes)) return;
48323
+ const pluginsAttribute = findEffectiveExplicitAttribute(node.attributes, "rehypePlugins");
48324
+ if (!pluginsAttribute) return;
48325
+ const pluginsExpression = getAttributeExpression(pluginsAttribute);
48326
+ if (!pluginsExpression) return;
48327
+ const pluginEntries = collectPluginEntries(pluginsExpression, context.scopes, /* @__PURE__ */ new Set());
48328
+ if (pluginEntries === null) return;
48329
+ if (!pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_RAW_MODULE, context.scopes, /* @__PURE__ */ new Set()))) return;
48330
+ if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
48331
+ context.report({
48332
+ node,
48333
+ message: MESSAGE$6
48334
+ });
48335
+ } }))
48336
+ });
48337
+ //#endregion
47322
48338
  //#region src/plugin/utils/collect-react-redux-selector-aliases.ts
47323
48339
  const REACT_REDUX_MODULE = "react-redux";
47324
48340
  const collectReactReduxSelectorAliases = (programRoot) => {
@@ -50215,7 +51231,7 @@ const isReactNativeDimensionsCallee = (node, callee) => {
50215
51231
  };
50216
51232
  const STYLE_FACTORY_CALLEE_PATTERN$1 = /(?:^|\.)(?:make|create)(?:Use)?Styles$/;
50217
51233
  const isInsideStyleFactoryCallback = (node) => {
50218
- const enclosingFunction = findEnclosingFunction(node);
51234
+ const enclosingFunction = findEnclosingFunction$1(node);
50219
51235
  if (!enclosingFunction) return false;
50220
51236
  const callExpression = enclosingFunction.parent;
50221
51237
  if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) return false;
@@ -55746,22 +56762,6 @@ const isInsideClassComponent = (node) => {
55746
56762
  }
55747
56763
  return false;
55748
56764
  };
55749
- const hasShortCircuitAncestor = (descendant, ancestor) => {
55750
- let current = descendant.parent;
55751
- while (current && current !== ancestor) {
55752
- if (isNodeOfType(current, "ConditionalExpression") && (isWithinRange(descendant, current.consequent) || isWithinRange(descendant, current.alternate))) return true;
55753
- if (isNodeOfType(current, "LogicalExpression") && (current.operator === "&&" || current.operator === "||" || current.operator === "??") && isWithinRange(descendant, current.right)) return true;
55754
- if (isNodeOfType(current, "AssignmentPattern") && isWithinRange(descendant, current.right)) return true;
55755
- current = current.parent ?? null;
55756
- }
55757
- return false;
55758
- };
55759
- const isWithinRange = (descendant, sibling) => {
55760
- const descendantSpan = descendant;
55761
- const siblingSpan = sibling;
55762
- if (typeof descendantSpan.start !== "number" || typeof siblingSpan.start !== "number" || typeof siblingSpan.end !== "number") return false;
55763
- return descendantSpan.start >= siblingSpan.start && (descendantSpan.end ?? siblingSpan.end) <= siblingSpan.end;
55764
- };
55765
56765
  const isInsideTry = (descendant, ancestor) => {
55766
56766
  let current = descendant.parent;
55767
56767
  while (current && current !== ancestor) {
@@ -55953,7 +56953,7 @@ const rulesOfHooks = defineRule({
55953
56953
  });
55954
56954
  return;
55955
56955
  }
55956
- if (hasShortCircuitAncestor(node, enclosing.node)) {
56956
+ if (isNodeConditionallyExecuted(node, enclosing.node)) {
55957
56957
  context.report({
55958
56958
  node: node.callee,
55959
56959
  message: buildConditionalMessage(hookName)
@@ -57087,12 +58087,14 @@ const declarationAwaitsRequestScopedCall = (declaration) => {
57087
58087
  }
57088
58088
  return false;
57089
58089
  };
57090
- const declarationAwaitsGate = (declaration) => {
58090
+ const declarationAwaitsGate = (declaration, context) => {
57091
58091
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
57092
58092
  for (const declarator of declaration.declarations ?? []) {
57093
58093
  if (!isNodeOfType(declarator.init, "AwaitExpression")) continue;
57094
58094
  const argument = declarator.init.argument;
57095
58095
  if (!isNodeOfType(argument, "CallExpression")) continue;
58096
+ if (hasPossibleStaticMemberCallWrite(argument, context.scopes)) return true;
58097
+ if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) continue;
57096
58098
  const calleeName = getCalleeName$2(argument);
57097
58099
  if (!calleeName) continue;
57098
58100
  if (isAuthGuardName(calleeName)) return true;
@@ -57120,7 +58122,7 @@ const serverSequentialIndependentAwait = defineRule({
57120
58122
  if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
57121
58123
  if (declarationAwaitsExistingPromise(nextStatement)) continue;
57122
58124
  if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
57123
- if (declarationAwaitsGate(currentStatement)) continue;
58125
+ if (declarationAwaitsGate(currentStatement, context)) continue;
57124
58126
  context.report({
57125
58127
  node: nextStatement,
57126
58128
  message: "This await doesn't use the previous result, so your users wait twice as long for nothing."
@@ -58106,7 +59108,7 @@ const tanstackStartNoNavigateInRender = defineRule({
58106
59108
  const isReturnedFromCustomHook = (functionNode) => {
58107
59109
  const parent = functionNode.parent;
58108
59110
  if (isNodeOfType(parent, "ReturnStatement")) {
58109
- const outerFunction = findEnclosingFunction(parent);
59111
+ const outerFunction = findEnclosingFunction$1(parent);
58110
59112
  const hookName = outerFunction ? getFunctionBindingName$1(outerFunction) : null;
58111
59113
  return Boolean(hookName && HOOK_NAME_PATTERN$3.test(hookName));
58112
59114
  }
@@ -58123,13 +59125,13 @@ const tanstackStartNoNavigateInRender = defineRule({
58123
59125
  return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
58124
59126
  };
58125
59127
  const isDeferredNavigateCall = (callNode) => {
58126
- let enclosingFunction = findEnclosingFunction(callNode);
59128
+ let enclosingFunction = findEnclosingFunction$1(callNode);
58127
59129
  while (enclosingFunction) {
58128
59130
  if (isDeferredCallbackPosition(enclosingFunction)) return true;
58129
59131
  if (isWiredAsEventHandler(enclosingFunction)) return true;
58130
59132
  if (isReturnedFromCustomHook(enclosingFunction)) return true;
58131
59133
  if (!isSynchronouslyInvokedAnonymousWrapper(enclosingFunction)) return false;
58132
- enclosingFunction = findEnclosingFunction(enclosingFunction);
59134
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
58133
59135
  }
58134
59136
  return false;
58135
59137
  };
@@ -62568,6 +63570,17 @@ const reactDoctorRules = [
62568
63570
  requires: [...new Set(["react", ...reactInJsxScope.requires ?? []])]
62569
63571
  }
62570
63572
  },
63573
+ {
63574
+ key: "react-doctor/react-markdown-unsanitized-raw-html",
63575
+ id: "react-markdown-unsanitized-raw-html",
63576
+ source: "react-doctor",
63577
+ originallyExternal: false,
63578
+ rule: {
63579
+ ...reactMarkdownUnsanitizedRawHtml,
63580
+ framework: "global",
63581
+ category: "Security"
63582
+ }
63583
+ },
62571
63584
  {
62572
63585
  key: "react-doctor/redux-useselector-inline-derivation",
62573
63586
  id: "redux-useselector-inline-derivation",