oxlint-plugin-react-doctor 0.7.7-dev.89dbf08 → 0.7.7-dev.9c612c5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1139 -376
  2. 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$1 = (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$1(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$1(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$1(node);
305
+ fileImportsReactRuntime = runtimeImports.hasReactRuntime;
306
+ fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
279
307
  };
280
308
  return wrappedVisitors;
281
309
  });
@@ -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
  };
@@ -5989,23 +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") && !isNodeOfType(node, "MemberExpression")) return null;
5995
- const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
5996
- if (node.computed) {
5997
- if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
5998
- if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
5999
- return null;
6000
- }
6001
- if (isNodeOfType(key, "Identifier")) return key.name;
6002
- if (isNodeOfType(key, "Literal")) {
6003
- if (typeof key.value === "string") return key.value;
6004
- if (options.stringifyNonStringLiterals) return String(key.value);
6005
- }
6006
- return null;
6007
- };
6008
- //#endregion
6009
6430
  //#region src/plugin/utils/are-expressions-structurally-equal.ts
6010
6431
  const areExpressionsStructurallyEqual = (a, b) => {
6011
6432
  if (!a || !b) return a === b;
@@ -6689,15 +7110,6 @@ const getDirectConstInitializer = (symbol) => {
6689
7110
  return symbol.initializer;
6690
7111
  };
6691
7112
  //#endregion
6692
- //#region src/plugin/utils/get-static-property-name.ts
6693
- const getStaticPropertyName = (memberExpression) => {
6694
- const property = memberExpression.property;
6695
- if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
6696
- if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
6697
- if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
6698
- return null;
6699
- };
6700
- //#endregion
6701
7113
  //#region src/plugin/utils/get-range-start.ts
6702
7114
  const getRangeStart = (node) => {
6703
7115
  const rangeStart = node.range?.[0];
@@ -7093,16 +7505,6 @@ const collectFunctionReturnStatements = (functionNode) => {
7093
7505
  return returnStatements;
7094
7506
  };
7095
7507
  //#endregion
7096
- //#region src/plugin/utils/find-enclosing-function.ts
7097
- const findEnclosingFunction = (node) => {
7098
- let cursor = node.parent;
7099
- while (cursor) {
7100
- if (isFunctionLike$2(cursor)) return cursor;
7101
- cursor = cursor.parent ?? null;
7102
- }
7103
- return null;
7104
- };
7105
- //#endregion
7106
7508
  //#region src/plugin/utils/statement-always-exits.ts
7107
7509
  const statementAlwaysExits = (statement) => {
7108
7510
  if (isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement")) return true;
@@ -7148,8 +7550,8 @@ const applyDefinitions = (incomingDefinitions, definitions) => {
7148
7550
  const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow) => {
7149
7551
  if (!REASSIGNABLE_BINDING_KINDS.has(symbol.kind)) return symbol.initializer ? [symbol.initializer] : [];
7150
7552
  if (!controlFlow) return [];
7151
- const referenceFunction = findEnclosingFunction(referenceNode);
7152
- if (findEnclosingFunction(symbol.bindingIdentifier) !== referenceFunction) return [];
7553
+ const referenceFunction = findEnclosingFunction$1(referenceNode);
7554
+ if (findEnclosingFunction$1(symbol.bindingIdentifier) !== referenceFunction) return [];
7153
7555
  if (!referenceFunction) return [];
7154
7556
  const functionControlFlow = controlFlow.cfgFor(referenceFunction);
7155
7557
  if (!functionControlFlow) return [];
@@ -7173,7 +7575,7 @@ const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow)
7173
7575
  if (symbol.initializer) addDefinition(symbol.initializer, symbol.bindingIdentifier, bindingPosition);
7174
7576
  for (const reference of symbol.references) {
7175
7577
  const writePosition = getRangeStart(reference.identifier);
7176
- 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;
7177
7579
  const assignedExpression = getAssignedExpressionForWrite(reference.identifier);
7178
7580
  if (!assignedExpression) continue;
7179
7581
  addDefinition(assignedExpression, reference.identifier, writePosition);
@@ -8845,6 +9247,7 @@ const HTML_FOR_ATTRIBUTE = "htmlFor";
8845
9247
  const LABEL_ELEMENT = "label";
8846
9248
  const LABEL_COMPONENT_NAME = "Label";
8847
9249
  const POLYMORPHIC_COMPONENT_PROP = "component";
9250
+ const TITLE_ATTRIBUTE = "title";
8848
9251
  const WRAPPER_LABEL_PROP = "label";
8849
9252
  const SELECT_ELEMENT = "select";
8850
9253
  const DEFAULT_DEPTH = 5;
@@ -8890,6 +9293,96 @@ const hasNonEmptyPropValue = (attribute) => {
8890
9293
  }
8891
9294
  return true;
8892
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
+ };
8893
9386
  const toAttributeMatchKey = (kind, value) => {
8894
9387
  const trimmedValue = value.trim();
8895
9388
  return trimmedValue.length > 0 ? `${kind}:${trimmedValue}` : null;
@@ -9146,6 +9639,7 @@ const controlHasAssociatedLabel = defineRule({
9146
9639
  if (typeValue === "submit" || typeValue === "reset") return;
9147
9640
  if (typeValue === "button" && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "value"))) return;
9148
9641
  }
9642
+ if (isDomElement && hasNonEmptyNativeTitle(getLastJsxPropIgnoreCase(opening.attributes, TITLE_ATTRIBUTE), context.scopes)) return;
9149
9643
  if (supportsPlaceholderNameFallback(tagName, opening) && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "placeholder"))) return;
9150
9644
  if (hasLabellingProp(opening.attributes, settings.labelAttributes)) return;
9151
9645
  if (isInsideJsxAttribute(node)) return;
@@ -11095,7 +11589,7 @@ const componentOrHookDisplayNameForFunction = (functionNode) => {
11095
11589
  //#endregion
11096
11590
  //#region src/plugin/utils/enclosing-component-or-hook-name.ts
11097
11591
  const enclosingComponentOrHookName = (node) => {
11098
- const functionNode = findEnclosingFunction(node);
11592
+ const functionNode = findEnclosingFunction$1(node);
11099
11593
  return functionNode ? componentOrHookDisplayNameForFunction(functionNode) : null;
11100
11594
  };
11101
11595
  //#endregion
@@ -11152,11 +11646,11 @@ const executesDuringRender = (functionNode, scopes) => {
11152
11646
  //#endregion
11153
11647
  //#region src/plugin/utils/find-render-phase-component-or-hook.ts
11154
11648
  const findRenderPhaseComponentOrHook = (node, scopes) => {
11155
- let functionNode = findEnclosingFunction(node);
11649
+ let functionNode = findEnclosingFunction$1(node);
11156
11650
  while (functionNode) {
11157
11651
  if (componentOrHookDisplayNameForFunction(functionNode)) return functionNode;
11158
11652
  if (!executesDuringRender(functionNode, scopes)) return null;
11159
- functionNode = findEnclosingFunction(functionNode);
11653
+ functionNode = findEnclosingFunction$1(functionNode);
11160
11654
  }
11161
11655
  return null;
11162
11656
  };
@@ -11405,13 +11899,13 @@ const findSubscribeLikeUsages = (callback, context) => {
11405
11899
  };
11406
11900
  const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, context) => {
11407
11901
  let pathAnchor = usageNode;
11408
- let pathOwner = findEnclosingFunction(pathAnchor);
11902
+ let pathOwner = findEnclosingFunction$1(pathAnchor);
11409
11903
  while (pathOwner && isSynchronousIteratorCallback(pathOwner)) {
11410
11904
  if (matchingNodes.length > 0 && matchingNodes.every((matchingNode) => context.cfg.enclosingFunction(matchingNode) === pathOwner)) break;
11411
11905
  const iteratorCall = pathOwner.parent;
11412
11906
  if (!isNodeOfType(iteratorCall, "CallExpression")) break;
11413
11907
  pathAnchor = iteratorCall;
11414
- pathOwner = findEnclosingFunction(pathAnchor);
11908
+ pathOwner = findEnclosingFunction$1(pathAnchor);
11415
11909
  }
11416
11910
  const owner = context.cfg.enclosingFunction(pathAnchor);
11417
11911
  if (!owner) return false;
@@ -11640,7 +12134,7 @@ const findCollectionMappingCall = (callbackNode) => {
11640
12134
  return callee.property.name === "map" && callNode.arguments?.[0] === callbackNode ? callNode : null;
11641
12135
  };
11642
12136
  const findMappedResourceCollectionKey = (resourceNode, context) => {
11643
- const callbackNode = findEnclosingFunction(resourceNode);
12137
+ const callbackNode = findEnclosingFunction$1(resourceNode);
11644
12138
  if (!callbackNode || !isNodeOfType(callbackNode, "ArrowFunctionExpression") && !isNodeOfType(callbackNode, "FunctionExpression")) return null;
11645
12139
  const mappingCall = findCollectionMappingCall(callbackNode);
11646
12140
  if (!mappingCall) return null;
@@ -11751,10 +12245,10 @@ const findSingleDirectInvocation = (functionNode, caller, context) => {
11751
12245
  });
11752
12246
  if (invocationCalls.length !== 1) return null;
11753
12247
  const invocationCall = invocationCalls[0];
11754
- return findEnclosingFunction(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
12248
+ return findEnclosingFunction$1(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
11755
12249
  };
11756
12250
  const resolveCleanupPathAnchor = (usageNode, effectCallback, context) => {
11757
- const usageFunction = findEnclosingFunction(usageNode);
12251
+ const usageFunction = findEnclosingFunction$1(usageNode);
11758
12252
  if (!usageFunction || usageFunction === effectCallback) return usageNode;
11759
12253
  return findSingleDirectInvocation(usageFunction, effectCallback, context) ?? usageNode;
11760
12254
  };
@@ -11769,7 +12263,7 @@ const resolveSingleAssignedCleanupFunction = (expression, usage, context) => {
11769
12263
  const assignmentReference = assignmentReferences[0];
11770
12264
  const assignmentTarget = findTransparentExpressionRoot(assignmentReference.identifier);
11771
12265
  const assignmentNode = assignmentTarget.parent;
11772
- 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;
11773
12267
  const assignedValue = stripParenExpression(assignmentNode.right);
11774
12268
  return isFunctionLike$2(assignedValue) ? assignedValue : null;
11775
12269
  };
@@ -11858,12 +12352,12 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
11858
12352
  return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
11859
12353
  };
11860
12354
  const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
11861
- const componentFunction = findEnclosingFunction(callback);
12355
+ const componentFunction = findEnclosingFunction$1(callback);
11862
12356
  if (!componentFunction || !isNodeOfType(componentFunction, "ArrowFunctionExpression") && !isNodeOfType(componentFunction, "FunctionExpression") && !isNodeOfType(componentFunction, "FunctionDeclaration")) return false;
11863
12357
  let didFindUnmountCleanup = false;
11864
12358
  walkAst(componentFunction.body, (child) => {
11865
12359
  if (didFindUnmountCleanup) return false;
11866
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction) return;
12360
+ if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction) return;
11867
12361
  if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
11868
12362
  const dependencyList = child.arguments?.[1];
11869
12363
  if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
@@ -11959,7 +12453,7 @@ const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, co
11959
12453
  return didWriteGuard;
11960
12454
  };
11961
12455
  const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
11962
- const usageFunction = findEnclosingFunction(usage.node);
12456
+ const usageFunction = findEnclosingFunction$1(usage.node);
11963
12457
  const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
11964
12458
  if (usage.handleKey === null || !usageFunction || usageFunction === callback || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
11965
12459
  return collectDeferredUsageGuardStates(usageFunction, usage.node, context).some((guardState) => !deferredUsageWritesGuardBeforeUsage(usageFunction, usage.node, guardState, context) && cleanupReturns.every((cleanupReturn) => cleanupReturnInvalidatesGuard(cleanupReturn, guardState, context)));
@@ -11967,7 +12461,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
11967
12461
  const effectHasCleanupForUsage = (callback, usage, context) => {
11968
12462
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
11969
12463
  if (callback.async) return false;
11970
- 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;
11971
12465
  if (!isNodeOfType(callback.body, "BlockStatement")) return callback.body === usage.node && isCleanupReturningSubscribeLikeCallExpression(callback.body);
11972
12466
  const matchingCleanupReturns = [];
11973
12467
  walkInsideStatementBlocks(callback.body, (child) => {
@@ -12136,7 +12630,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
12136
12630
  parentNode = currentNode.parent;
12137
12631
  }
12138
12632
  if (!isNodeOfType(parentNode, "ReturnStatement") || parentNode.argument !== currentNode) return false;
12139
- const effectCallback = findEnclosingFunction(parentNode);
12633
+ const effectCallback = findEnclosingFunction$1(parentNode);
12140
12634
  const effectCall = effectCallback?.parent;
12141
12635
  return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
12142
12636
  };
@@ -12146,13 +12640,13 @@ const isPotentiallyReachableFunction = (functionNode, context) => {
12146
12640
  if (!bindingIdentifier) return false;
12147
12641
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12148
12642
  if (!symbol) return false;
12149
- return symbol.references.some((reference) => findEnclosingFunction(reference.identifier) !== functionNode);
12643
+ return symbol.references.some((reference) => findEnclosingFunction$1(reference.identifier) !== functionNode);
12150
12644
  };
12151
12645
  const isReleaseReachableForUsage = (releaseNode, usage, context) => {
12152
12646
  if (!isNodeReachableWithinFunction(releaseNode, context)) return false;
12153
- const releaseFunction = findEnclosingFunction(releaseNode);
12647
+ const releaseFunction = findEnclosingFunction$1(releaseNode);
12154
12648
  if (!releaseFunction) return true;
12155
- if (releaseFunction === findEnclosingFunction(usage.node)) return true;
12649
+ if (releaseFunction === findEnclosingFunction$1(usage.node)) return true;
12156
12650
  return isPotentiallyReachableFunction(releaseFunction, context);
12157
12651
  };
12158
12652
  const fileContainsReleaseForUsage = (usage, context) => {
@@ -12335,10 +12829,10 @@ const cleanupFunctionReleasesRefOwnedUsage = (cleanupFunction, componentFunction
12335
12829
  }
12336
12830
  if (assignedKey !== storage.refCurrentKey) return;
12337
12831
  const assignedValue = stripParenExpression(child.right);
12338
- if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction(child) === cleanupFunction) return;
12832
+ if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction$1(child) === cleanupFunction) return;
12339
12833
  const assignedSessionProperties = (isNodeOfType(assignedValue, "ObjectExpression") ? assignedValue : null)?.properties ?? [];
12340
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);
12341
- if (findEnclosingFunction(child) !== retainedFunction || !storesMatchingHandler) {
12835
+ if (findEnclosingFunction$1(child) !== retainedFunction || !storesMatchingHandler) {
12342
12836
  hasUnsafeRefWrite = true;
12343
12837
  return false;
12344
12838
  }
@@ -12359,12 +12853,12 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
12359
12853
  return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
12360
12854
  };
12361
12855
  const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
12362
- const componentFunction = findEnclosingFunction(retainedFunction);
12856
+ const componentFunction = findEnclosingFunction$1(retainedFunction);
12363
12857
  if (!componentFunction || !isFunctionLike$2(componentFunction)) return false;
12364
12858
  let didFindCleanupEffect = false;
12365
12859
  walkAst(componentFunction.body, (child) => {
12366
12860
  if (didFindCleanupEffect) return false;
12367
- 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;
12368
12862
  const effectStatement = findTransparentExpressionRoot(child).parent;
12369
12863
  if (!isNodeOfType(effectStatement, "ExpressionStatement") || effectStatement.parent !== componentFunction.body) return;
12370
12864
  const effectCallback = getEffectCallback(child);
@@ -13218,6 +13712,202 @@ const resolveExhaustiveDepsSettings = (settings) => {
13218
13712
  };
13219
13713
  };
13220
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
13221
13911
  //#region src/plugin/rules/react-builtins/exhaustive-deps-suppression.ts
13222
13912
  const DISABLE_COMMENT_RULE_NAME_PATTERN$1 = /(?:^|[\s,/])exhaustive-deps(?:$|[\s,:])/;
13223
13913
  const DISABLE_NEXT_LINE_PATTERN$1 = /\b(?:eslint|oxlint)-disable-next-line\b([^\n]*)/;
@@ -13289,25 +13979,6 @@ const isExhaustiveDepsSuppressedAt = (filename, nodeStartOffset) => {
13289
13979
  return index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf16NewlineOffsets)) || index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf8NewlineOffsets));
13290
13980
  };
13291
13981
  //#endregion
13292
- //#region src/plugin/utils/is-ast-descendant.ts
13293
- /**
13294
- * True when `inner` is `outer` itself or any descendant in the AST
13295
- * `parent` chain. Walks `inner.parent` upward and stops at either a
13296
- * match (`true`) or the chain's root (`false`).
13297
- *
13298
- * Was duplicated byte-identical in:
13299
- * - exhaustive-deps-symbol-stability.ts
13300
- * - semantic/closure-captures.ts
13301
- */
13302
- const isAstDescendant = (inner, outer) => {
13303
- let current = inner;
13304
- while (current) {
13305
- if (current === outer) return true;
13306
- current = current.parent ?? null;
13307
- }
13308
- return false;
13309
- };
13310
- //#endregion
13311
13982
  //#region src/plugin/rules/react-builtins/exhaustive-deps-symbol-stability.ts
13312
13983
  /**
13313
13984
  * Symbol-stability helpers consumed by the `exhaustive-deps` rule.
@@ -13488,6 +14159,7 @@ const EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS = new Set([
13488
14159
  "useLayoutEffect",
13489
14160
  "useInsertionEffect"
13490
14161
  ]);
14162
+ const SOLE_WRITER_GUARD_HOOKS = new Set(["useEffect", "useLayoutEffect"]);
13491
14163
  const buildAdditionalHooksRegex = (additional) => {
13492
14164
  if (!additional) return null;
13493
14165
  try {
@@ -13624,7 +14296,7 @@ const stringifyMemberChain = (node) => {
13624
14296
  }
13625
14297
  return null;
13626
14298
  };
13627
- const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys) => {
14299
+ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys, allowSoleWriterEffectGuards = false) => {
13628
14300
  const keys = /* @__PURE__ */ new Set();
13629
14301
  const stableCapturedNames = /* @__PURE__ */ new Set();
13630
14302
  const moduleScopeCapturedNames = /* @__PURE__ */ new Set();
@@ -13635,6 +14307,10 @@ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys) => {
13635
14307
  const symbol = reference.resolvedSymbol;
13636
14308
  if (!symbol) continue;
13637
14309
  if (isRecursiveInitializerCapture(symbol, callback)) continue;
14310
+ if (allowSoleWriterEffectGuards && isSoleWriterEffectGuardCapture(symbol, callback, scopes)) {
14311
+ stableCapturedNames.add(symbol.name);
14312
+ continue;
14313
+ }
13638
14314
  if (symbolHasStableValue(symbol, scopes)) {
13639
14315
  stableCapturedNames.add(symbol.name);
13640
14316
  continue;
@@ -14307,7 +14983,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
14307
14983
  if (isNodeOfType(stripped, "Identifier")) declaredExactBindingKeys.add(key);
14308
14984
  declaredKeyToReportNode.set(key, elementNode);
14309
14985
  }
14310
- 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));
14311
14987
  for (const forcedCaptureKey of forcedCaptureKeys) captureKeys.add(forcedCaptureKey);
14312
14988
  addAggregatePropsDependency(captureKeys, declaredKeys, callbackToAnalyze ?? callbackArgument, context.scopes);
14313
14989
  const missingCaptureKeys = [];
@@ -16121,14 +16797,14 @@ const isBindingInvokedOnRenderPath = (root, bindingName) => {
16121
16797
  if (didFindRenderPathInvocation) return false;
16122
16798
  if (!isNodeOfType(node, "CallExpression")) return;
16123
16799
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== bindingName) return;
16124
- let enclosingFunction = findEnclosingFunction(node);
16800
+ let enclosingFunction = findEnclosingFunction$1(node);
16125
16801
  while (enclosingFunction) {
16126
16802
  if (isDeferredCallbackPosition$1(enclosingFunction) || getHandlerNamedBindingName(enclosingFunction)) return;
16127
16803
  if (containingFunctionIsComponentOrHook(enclosingFunction)) {
16128
16804
  didFindRenderPathInvocation = true;
16129
16805
  return;
16130
16806
  }
16131
- enclosingFunction = findEnclosingFunction(enclosingFunction);
16807
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
16132
16808
  }
16133
16809
  });
16134
16810
  return didFindRenderPathInvocation;
@@ -16163,7 +16839,7 @@ const jotaiSelectAtomInRenderBody = defineRule({
16163
16839
  };
16164
16840
  return { CallExpression(node) {
16165
16841
  if (!isImportedSelectAtom(node)) return;
16166
- const nearestFunctionLike = findEnclosingFunction(node);
16842
+ const nearestFunctionLike = findEnclosingFunction$1(node);
16167
16843
  if (!nearestFunctionLike) return;
16168
16844
  if (isDeferredCallback(nearestFunctionLike)) return;
16169
16845
  if (isBindingUsedAsHandler(nearestFunctionLike)) return;
@@ -17160,7 +17836,7 @@ const isPersistentCacheSetWrite = (callExpression, newExpression, enclosingFunct
17160
17836
  return !isAstDescendant(receiverBinding.scopeOwner, enclosingFunction);
17161
17837
  };
17162
17838
  const isInsideCacheMemo = (node) => {
17163
- const enclosingFunction = findEnclosingFunction(node);
17839
+ const enclosingFunction = findEnclosingFunction$1(node);
17164
17840
  let child = node;
17165
17841
  let cursor = node.parent ?? null;
17166
17842
  while (cursor) {
@@ -17198,7 +17874,7 @@ const getFunctionName = (functionNode) => {
17198
17874
  return null;
17199
17875
  };
17200
17876
  const isUncacheableOptionsMergeUtility = (node) => {
17201
- const enclosingFunction = findEnclosingFunction(node);
17877
+ const enclosingFunction = findEnclosingFunction$1(node);
17202
17878
  if (!enclosingFunction || !isFunctionLike$2(enclosingFunction)) return false;
17203
17879
  const functionName = getFunctionName(enclosingFunction);
17204
17880
  if (!functionName || isComponentOrHookName(functionName)) return false;
@@ -17389,13 +18065,6 @@ const isSafeStatefulReplaceAllSearch = (node, flags, context) => {
17389
18065
  const callee = stripParenExpression(replaceAllCall.callee);
17390
18066
  return isNodeOfType(callee, "MemberExpression") && !callee.computed && !callee.optional && !replaceAllCall.optional && isNodeOfType(callee.property, "Identifier") && callee.property.name === "replaceAll" && isProvenNativeStringReceiver(callee.object, context);
17391
18067
  };
17392
- const getDestructuredBindingPropertyName = (bindingIdentifier) => {
17393
- let bindingNode = bindingIdentifier;
17394
- if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
17395
- const property = bindingNode.parent;
17396
- if (!isNodeOfType(property, "Property") || property.value !== bindingNode || !isNodeOfType(property.parent, "ObjectPattern")) return null;
17397
- return getStaticPropertyKeyName(property, { allowComputedString: true });
17398
- };
17399
18068
  const extendGlobalPath = (basePath, propertyName) => {
17400
18069
  if (basePath === "global") {
17401
18070
  if (propertyName === null || GLOBAL_OBJECT_NAMES.has(propertyName)) return "global";
@@ -17685,7 +18354,89 @@ const collectEarlierAndGuardOperands = (node) => {
17685
18354
  return earlierOperands;
17686
18355
  };
17687
18356
  //#endregion
18357
+ //#region src/plugin/utils/unwrap-object-integrity-expression.ts
18358
+ const OBJECT_INTEGRITY_METHOD_NAMES = new Set([
18359
+ "freeze",
18360
+ "seal",
18361
+ "preventExtensions"
18362
+ ]);
18363
+ const OBJECT_FREEZE_OR_SEAL_METHOD_NAMES = new Set(["freeze", "seal"]);
18364
+ const getObjectIntegrityMethodName = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
18365
+ const expression = stripParenExpression(node);
18366
+ if (!isNodeOfType(expression, "CallExpression")) return null;
18367
+ const callee = stripParenExpression(expression.callee);
18368
+ const receiver = isNodeOfType(callee, "MemberExpression") ? stripParenExpression(callee.object) : callee;
18369
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || !scopes.isGlobalReference(receiver) || !isNodeOfType(callee.property, "Identifier") || !methodNames.has(callee.property.name)) return null;
18370
+ return callee.property.name;
18371
+ };
18372
+ const unwrapObjectIntegrityExpression = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
18373
+ let expression = stripParenExpression(node);
18374
+ while (isNodeOfType(expression, "CallExpression")) {
18375
+ if (!getObjectIntegrityMethodName(expression, scopes, methodNames)) break;
18376
+ const wrappedExpression = expression.arguments[0];
18377
+ if (!wrappedExpression || isNodeOfType(wrappedExpression, "SpreadElement")) break;
18378
+ expression = stripParenExpression(wrappedExpression);
18379
+ }
18380
+ return expression;
18381
+ };
18382
+ //#endregion
17688
18383
  //#region src/plugin/rules/js-performance/js-length-check-first.ts
18384
+ const OBJECT_CARDINALITY_PROJECTION_METHOD_NAMES = new Set(["keys", "values"]);
18385
+ const getGlobalObjectProjection = (expression, context) => {
18386
+ const callExpression = stripParenExpression(expression);
18387
+ if (!isNodeOfType(callExpression, "CallExpression")) return null;
18388
+ const callee = stripParenExpression(callExpression.callee);
18389
+ if (!isNodeOfType(callee, "MemberExpression")) return null;
18390
+ const receiver = stripParenExpression(callee.object);
18391
+ if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || !context.scopes.isGlobalReference(receiver)) return null;
18392
+ const methodName = getStaticPropertyName(callee);
18393
+ if (!methodName || !OBJECT_CARDINALITY_PROJECTION_METHOD_NAMES.has(methodName)) return null;
18394
+ const callArguments = callExpression.arguments ?? [];
18395
+ if (callArguments.length !== 1 || isNodeOfType(callArguments[0], "SpreadElement")) return null;
18396
+ return {
18397
+ methodName,
18398
+ source: callArguments[0]
18399
+ };
18400
+ };
18401
+ const isPlainDataObjectLiteral = (expression) => {
18402
+ const objectExpression = stripParenExpression(expression);
18403
+ if (!isNodeOfType(objectExpression, "ObjectExpression")) return false;
18404
+ return (objectExpression.properties ?? []).every((property) => {
18405
+ if (!isNodeOfType(property, "Property") || property.kind !== "init" || property.method === true) return false;
18406
+ const propertyValue = stripParenExpression(property.value);
18407
+ return isNodeOfType(propertyValue, "Literal") || isNodeOfType(propertyValue, "TemplateLiteral") && propertyValue.expressions.length === 0;
18408
+ });
18409
+ };
18410
+ const getGlobalObjectFreezeArgument = (expression, context) => {
18411
+ const callExpression = stripParenExpression(expression);
18412
+ if (!isNodeOfType(callExpression, "CallExpression")) return null;
18413
+ if (getObjectIntegrityMethodName(callExpression, context.scopes) !== "freeze") return null;
18414
+ const callArguments = callExpression.arguments ?? [];
18415
+ if (callArguments.length !== 1 || isNodeOfType(callArguments[0], "SpreadElement")) return null;
18416
+ return callArguments[0];
18417
+ };
18418
+ const resolveStablePlainObjectRootId = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
18419
+ const source = stripParenExpression(expression);
18420
+ if (!isNodeOfType(source, "Identifier")) return null;
18421
+ const symbol = context.scopes.symbolFor(source);
18422
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
18423
+ const directConstInitializer = getDirectConstInitializer(symbol);
18424
+ if (!directConstInitializer) return null;
18425
+ visitedSymbolIds.add(symbol.id);
18426
+ const initializer = stripParenExpression(directConstInitializer);
18427
+ const frozenValue = getGlobalObjectFreezeArgument(initializer, context);
18428
+ if (frozenValue) return isPlainDataObjectLiteral(frozenValue) ? symbol.id : null;
18429
+ return resolveStablePlainObjectRootId(initializer, context, visitedSymbolIds);
18430
+ };
18431
+ const areEqualCardinalityObjectProjections = (receiverArray, indexedArray, context) => {
18432
+ const receiverProjection = getGlobalObjectProjection(receiverArray, context);
18433
+ const indexedProjection = getGlobalObjectProjection(indexedArray, context);
18434
+ if (!receiverProjection || !indexedProjection) return false;
18435
+ if (receiverProjection.methodName === indexedProjection.methodName) return false;
18436
+ const receiverRootId = resolveStablePlainObjectRootId(receiverProjection.source, context);
18437
+ const indexedRootId = resolveStablePlainObjectRootId(indexedProjection.source, context);
18438
+ return receiverRootId !== null && receiverRootId === indexedRootId;
18439
+ };
17689
18440
  const findIndexedArrayObject = (callbackBody, indexParameterName) => {
17690
18441
  let indexedArrayObject = null;
17691
18442
  walkAst(callbackBody, (child) => {
@@ -17900,6 +18651,7 @@ const jsLengthCheckFirst = defineRule({
17900
18651
  const resolvedReceiverSource = resolveComparedArraySource(receiverArrayObject, node);
17901
18652
  const resolvedIndexedSource = resolveComparedArraySource(indexedArrayObject, node);
17902
18653
  if (areExpressionsStructurallyEqual(resolvedReceiverSource, resolvedIndexedSource)) return;
18654
+ if (areEqualCardinalityObjectProjections(receiverArrayObject, indexedArrayObject, context) || areEqualCardinalityObjectProjections(resolvedReceiverSource, resolvedIndexedSource, context)) return;
17903
18655
  const comparedArrayPairs = [[receiverArrayObject, indexedArrayObject], [resolvedReceiverSource, resolvedIndexedSource]];
17904
18656
  if (collectEarlierAndGuardOperands(node).some((guardOperand) => comparedArrayPairs.some(([receiverSide, indexedSide]) => isLengthComparison(guardOperand, receiverSide, indexedSide, LENGTH_ANY_COMPARISON_OPERATORS)))) return;
17905
18657
  if (collectEarlierOrOperands(node).some((guardOperand) => comparedArrayPairs.some(([receiverSide, indexedSide]) => isLengthComparison(guardOperand, receiverSide, indexedSide, LENGTH_MISMATCH_OPERATORS)))) return;
@@ -23777,32 +24529,6 @@ const hasDirective = (programNode, directive) => {
23777
24529
  return Boolean(programNode.body?.some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === directive));
23778
24530
  };
23779
24531
  //#endregion
23780
- //#region src/plugin/utils/unwrap-object-integrity-expression.ts
23781
- const OBJECT_INTEGRITY_METHOD_NAMES = new Set([
23782
- "freeze",
23783
- "seal",
23784
- "preventExtensions"
23785
- ]);
23786
- const OBJECT_FREEZE_OR_SEAL_METHOD_NAMES = new Set(["freeze", "seal"]);
23787
- const getObjectIntegrityMethodName = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
23788
- const expression = stripParenExpression(node);
23789
- if (!isNodeOfType(expression, "CallExpression")) return null;
23790
- const callee = stripParenExpression(expression.callee);
23791
- const receiver = isNodeOfType(callee, "MemberExpression") ? stripParenExpression(callee.object) : callee;
23792
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || !scopes.isGlobalReference(receiver) || !isNodeOfType(callee.property, "Identifier") || !methodNames.has(callee.property.name)) return null;
23793
- return callee.property.name;
23794
- };
23795
- const unwrapObjectIntegrityExpression = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
23796
- let expression = stripParenExpression(node);
23797
- while (isNodeOfType(expression, "CallExpression")) {
23798
- if (!getObjectIntegrityMethodName(expression, scopes, methodNames)) break;
23799
- const wrappedExpression = expression.arguments[0];
23800
- if (!wrappedExpression || isNodeOfType(wrappedExpression, "SpreadElement")) break;
23801
- expression = stripParenExpression(wrappedExpression);
23802
- }
23803
- return expression;
23804
- };
23805
- //#endregion
23806
24532
  //#region src/plugin/rules/nextjs/nextjs-async-client-component.ts
23807
24533
  const nextjsAsyncClientComponent = defineRule({
23808
24534
  id: "nextjs-async-client-component",
@@ -26142,7 +26868,7 @@ const isIndependentWriterIdentifier = (componentFunction, identifier, includeDef
26142
26868
  };
26143
26869
  const hasUserInputSetterWriter = (setterRef, effectNode, includeDeferredWriters = false) => {
26144
26870
  if (!setterRef.resolved) return false;
26145
- const componentFunction = findEnclosingFunction(effectNode);
26871
+ const componentFunction = findEnclosingFunction$1(effectNode);
26146
26872
  if (!componentFunction) return false;
26147
26873
  for (const reference of setterRef.resolved.references) {
26148
26874
  if (reference.init) continue;
@@ -28915,138 +29641,6 @@ const noBarrelImport = defineRule({
28915
29641
  }
28916
29642
  });
28917
29643
  //#endregion
28918
- //#region src/plugin/utils/has-static-property-write-before.ts
28919
- const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
28920
- const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
28921
- "CatchClause",
28922
- "ConditionalExpression",
28923
- "DoWhileStatement",
28924
- "ForInStatement",
28925
- "ForOfStatement",
28926
- "ForStatement",
28927
- "IfStatement",
28928
- "LogicalExpression",
28929
- "SwitchCase",
28930
- "SwitchStatement",
28931
- "TryStatement",
28932
- "WhileStatement"
28933
- ]);
28934
- const getResolvedStaticPropertyName = (memberExpression, scopes) => {
28935
- if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
28936
- const directPropertyName = getStaticPropertyName(memberExpression);
28937
- if (directPropertyName || !memberExpression.computed) return directPropertyName;
28938
- const property = stripParenExpression(memberExpression.property);
28939
- if (!isNodeOfType(property, "Identifier")) return null;
28940
- const propertySymbol = resolveConstIdentifierAlias(property, scopes);
28941
- const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
28942
- return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
28943
- };
28944
- const collectScopeSymbols = (scope, symbols) => {
28945
- symbols.push(...scope.symbols);
28946
- for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
28947
- };
28948
- const getEquivalentSymbols = (identifier, scopes) => {
28949
- const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
28950
- if (!rootSymbol) return [];
28951
- let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
28952
- if (!symbolsByRootId) {
28953
- symbolsByRootId = /* @__PURE__ */ new Map();
28954
- equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
28955
- }
28956
- const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
28957
- if (cachedSymbols) return cachedSymbols;
28958
- const allSymbols = [];
28959
- collectScopeSymbols(scopes.rootScope, allSymbols);
28960
- const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
28961
- symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
28962
- return equivalentSymbols;
28963
- };
28964
- const findExecutionBoundary = (node) => {
28965
- let current = node;
28966
- while (current) {
28967
- if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
28968
- current = current.parent ?? null;
28969
- }
28970
- return null;
28971
- };
28972
- const isOnUnconditionalPath = (node, boundary) => {
28973
- let current = node.parent ?? null;
28974
- while (current && current !== boundary) {
28975
- if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
28976
- current = current.parent ?? null;
28977
- }
28978
- return current === boundary;
28979
- };
28980
- const findFunctionBindingIdentifier = (functionNode) => {
28981
- if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
28982
- const parent = functionNode.parent;
28983
- if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
28984
- return null;
28985
- };
28986
- const findDirectCall = (identifier) => {
28987
- let callee = identifier;
28988
- let parent = callee.parent;
28989
- while (parent && stripParenExpression(parent) === identifier) {
28990
- callee = parent;
28991
- parent = callee.parent;
28992
- }
28993
- return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
28994
- };
28995
- const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
28996
- if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
28997
- visitedFunctionNodes.add(functionNode);
28998
- const referenceBoundary = findExecutionBoundary(referenceNode);
28999
- if (!referenceBoundary) return false;
29000
- const invocationCalls = [];
29001
- const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
29002
- if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
29003
- const call = findDirectCall(reference.identifier);
29004
- if (call) invocationCalls.push(call);
29005
- }
29006
- else {
29007
- const call = findDirectCall(functionNode);
29008
- if (call) invocationCalls.push(call);
29009
- }
29010
- return invocationCalls.some((call) => {
29011
- if (call.range[0] >= referenceNode.range[0]) return false;
29012
- const callBoundary = findExecutionBoundary(call);
29013
- if (!callBoundary) return false;
29014
- if (callBoundary === referenceBoundary) return true;
29015
- if (!isFunctionLike$2(callBoundary)) return false;
29016
- return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
29017
- });
29018
- };
29019
- const isMemberWriteTarget = (memberExpression) => {
29020
- const parent = memberExpression.parent;
29021
- if (!parent) return false;
29022
- if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
29023
- if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
29024
- return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
29025
- };
29026
- const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
29027
- let parent = reference.identifier.parent;
29028
- while (parent && stripParenExpression(parent) === reference.identifier) parent = parent.parent;
29029
- if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== reference.identifier || getResolvedStaticPropertyName(parent, scopes) !== propertyName || !isMemberWriteTarget(parent)) return false;
29030
- const writeBoundary = findExecutionBoundary(parent);
29031
- const referenceBoundary = findExecutionBoundary(referenceNode);
29032
- if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(parent, writeBoundary)) return false;
29033
- if (writeBoundary === referenceBoundary) return parent.range[0] < referenceNode.range[0];
29034
- return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
29035
- });
29036
- const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
29037
- if (!isNodeOfType(identifier, "Identifier")) return false;
29038
- return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
29039
- };
29040
- //#endregion
29041
- //#region src/plugin/utils/has-symbol-write-before.ts
29042
- const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
29043
- if (reference.flag === "read") return false;
29044
- const writeFunction = findEnclosingFunction(reference.identifier);
29045
- if (writeFunction === findEnclosingFunction(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
29046
- if (!writeFunction) return true;
29047
- return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
29048
- });
29049
- //#endregion
29050
29644
  //#region src/plugin/utils/has-stable-call-target.ts
29051
29645
  const hasStableCallTarget = (callExpression, scopes) => {
29052
29646
  if (!isNodeOfType(callExpression, "CallExpression")) return false;
@@ -29515,8 +30109,8 @@ const isUseMemoCallbackArgument = (functionNode) => {
29515
30109
  return isReactFunctionCall(parent, "useMemo");
29516
30110
  };
29517
30111
  const findEnclosingRenderFunction = (node) => {
29518
- let enclosingFunction = findEnclosingFunction(node);
29519
- while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
30112
+ let enclosingFunction = findEnclosingFunction$1(node);
30113
+ while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
29520
30114
  return enclosingFunction;
29521
30115
  };
29522
30116
  const noCreateRefInFunctionComponent = defineRule({
@@ -30567,7 +31161,7 @@ const getEnclosingEffectHookCallback = (node, componentFunction) => {
30567
31161
  const isEffectDrivenResync = (useStateCall) => {
30568
31162
  const setterName = getStateSetterName(useStateCall);
30569
31163
  if (!setterName) return false;
30570
- const componentFunction = findEnclosingFunction(useStateCall);
31164
+ const componentFunction = findEnclosingFunction$1(useStateCall);
30571
31165
  if (!componentFunction) return false;
30572
31166
  let isExempt = false;
30573
31167
  walkAst(componentFunction, (child) => {
@@ -30671,7 +31265,7 @@ const isDraftReseedOrRenderAdjusted = (useStateCall, isPropName) => {
30671
31265
  const setterName = getStateSetterName(useStateCall);
30672
31266
  if (!setterName) return false;
30673
31267
  const stateValueName = getStateValueName(useStateCall);
30674
- const componentFunction = findEnclosingFunction(useStateCall);
31268
+ const componentFunction = findEnclosingFunction$1(useStateCall);
30675
31269
  if (!componentFunction) return false;
30676
31270
  let isExempt = false;
30677
31271
  walkAst(componentFunction, (child) => {
@@ -30717,7 +31311,7 @@ const noDerivedUseState = defineRule({
30717
31311
  if (isDraftReseedOrRenderAdjusted(node, propStackTracker.isPropName)) return;
30718
31312
  if (isEffectDrivenResync(node)) return;
30719
31313
  if (isNextjsDataFetchingPage(node)) return;
30720
- const componentFunction = findEnclosingFunction(node);
31314
+ const componentFunction = findEnclosingFunction$1(node);
30721
31315
  if (componentFunction) {
30722
31316
  if (isDefaultedDestructuredProp(componentFunction, propName)) return;
30723
31317
  if (isDraftCommittedToParent(componentFunction, getStateValueName(node), propStackTracker.isPropName)) return;
@@ -32192,6 +32786,29 @@ const isReturnOnlyStatement = (node) => {
32192
32786
  return isNodeOfType(node, "BlockStatement") && (node.body?.length ?? 0) === 1 && isNodeOfType(node.body?.[0], "ReturnStatement");
32193
32787
  };
32194
32788
  const hasEventLikeRemainingStatements = (statements) => statements.some((statement) => !isNodeOfType(statement, "ReturnStatement") && hasEventLikeNode(statement));
32789
+ const collectLeadingEarlyReturnGuards = (statements) => {
32790
+ const guardExpressions = [];
32791
+ for (const statement of statements) {
32792
+ if (isNodeOfType(statement, "VariableDeclaration")) continue;
32793
+ if (!isNodeOfType(statement, "IfStatement") || statement.alternate || !isReturnOnlyStatement(statement.consequent)) break;
32794
+ collectGuardExpressions(statement.test, guardExpressions);
32795
+ }
32796
+ return guardExpressions;
32797
+ };
32798
+ const collectImmutableExpressionOrigins = (expression, scopes) => {
32799
+ const origins = [];
32800
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
32801
+ let currentExpression = stripParenExpression(expression);
32802
+ while (currentExpression) {
32803
+ origins.push(currentExpression);
32804
+ if (!isNodeOfType(currentExpression, "Identifier")) break;
32805
+ const bindingSymbol = scopes.symbolFor(currentExpression);
32806
+ if (!bindingSymbol || bindingSymbol.kind !== "const" || visitedSymbolIds.has(bindingSymbol.id) || !bindingSymbol.initializer) break;
32807
+ visitedSymbolIds.add(bindingSymbol.id);
32808
+ currentExpression = stripParenExpression(bindingSymbol.initializer);
32809
+ }
32810
+ return origins;
32811
+ };
32195
32812
  const doesGuardMatchDependency = (guardExpression, dependencyExpression) => {
32196
32813
  const unwrappedDependencyExpression = unwrapChainExpression$1(dependencyExpression);
32197
32814
  if (!unwrappedDependencyExpression) return false;
@@ -32199,6 +32816,69 @@ const doesGuardMatchDependency = (guardExpression, dependencyExpression) => {
32199
32816
  return isNodeOfType(unwrappedDependencyExpression, "Identifier") && unwrappedDependencyExpression.name === guardExpression.rootIdentifierName;
32200
32817
  };
32201
32818
  const hasDependencyMatch = (guardExpression, dependencyExpressions) => dependencyExpressions.some((dependencyExpression) => doesGuardMatchDependency(guardExpression, dependencyExpression));
32819
+ const hasAliasedDependencyMatch = (guardExpression, dependencyExpressions, scopes) => {
32820
+ const guardOrigins = collectImmutableExpressionOrigins(guardExpression.expression, scopes);
32821
+ return dependencyExpressions.some((dependencyExpression) => {
32822
+ if (!dependencyExpression) return false;
32823
+ const dependencyOrigins = collectImmutableExpressionOrigins(dependencyExpression, scopes);
32824
+ return guardOrigins.some((guardOrigin) => dependencyOrigins.some((dependencyOrigin) => areExpressionsStructurallyEqual(guardOrigin, dependencyOrigin)));
32825
+ });
32826
+ };
32827
+ const isStaticallyTrue = (expression, scopes) => collectImmutableExpressionOrigins(expression, scopes).some((origin) => isNodeOfType(origin, "Literal") && origin.value === true);
32828
+ const isReactRouterReplacementNavigation = (node, rootIdentifierNames, scopes) => {
32829
+ let didFindNavigation = false;
32830
+ let didFindOtherTriggeredSideEffect = false;
32831
+ walkAst(node, (child) => {
32832
+ if (!isNodeOfType(child, "CallExpression")) return;
32833
+ if (!(findTriggeredSideEffectCalleeName(child) !== null || hasDocumentClassListMutation(child))) return;
32834
+ const destinationExpression = child.arguments?.[0];
32835
+ const doesDestinationReferenceReconciliationValue = Boolean(destinationExpression && collectImmutableExpressionOrigins(destinationExpression, scopes).some((origin) => doesNodeReferenceAnyRoot(origin, rootIdentifierNames)));
32836
+ if (!isNodeOfType(child.callee, "Identifier") || !doesDestinationReferenceReconciliationValue) {
32837
+ didFindOtherTriggeredSideEffect = true;
32838
+ return;
32839
+ }
32840
+ const navigationSymbol = scopes.symbolFor(child.callee);
32841
+ const navigationInitializer = navigationSymbol?.initializer ? stripParenExpression(navigationSymbol.initializer) : null;
32842
+ if (navigationSymbol?.kind !== "const" || !isNodeOfType(navigationInitializer, "CallExpression") || !isNodeOfType(navigationInitializer.callee, "Identifier")) {
32843
+ didFindOtherTriggeredSideEffect = true;
32844
+ return;
32845
+ }
32846
+ if (scopes.symbolFor(navigationInitializer.callee)?.kind !== "import") {
32847
+ didFindOtherTriggeredSideEffect = true;
32848
+ return;
32849
+ }
32850
+ if ((getImportedNameFromModule(navigationInitializer.callee, navigationInitializer.callee.name, "react-router-dom") ?? getImportedNameFromModule(navigationInitializer.callee, navigationInitializer.callee.name, "react-router")) !== "useNavigate") {
32851
+ didFindOtherTriggeredSideEffect = true;
32852
+ return;
32853
+ }
32854
+ const navigationOptionsArgument = child.arguments?.[1];
32855
+ const navigationOptions = navigationOptionsArgument ? stripParenExpression(navigationOptionsArgument) : null;
32856
+ if (!isNodeOfType(navigationOptions, "ObjectExpression")) {
32857
+ didFindOtherTriggeredSideEffect = true;
32858
+ return;
32859
+ }
32860
+ let isReplacementGuaranteed = false;
32861
+ for (const property of navigationOptions.properties) {
32862
+ if (isNodeOfType(property, "SpreadElement")) {
32863
+ isReplacementGuaranteed = false;
32864
+ continue;
32865
+ }
32866
+ const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
32867
+ if (propertyName === null) {
32868
+ isReplacementGuaranteed = false;
32869
+ continue;
32870
+ }
32871
+ if (propertyName !== "replace") continue;
32872
+ isReplacementGuaranteed = isStaticallyTrue(property.value, scopes);
32873
+ }
32874
+ if (isReplacementGuaranteed) {
32875
+ didFindNavigation = true;
32876
+ return;
32877
+ }
32878
+ didFindOtherTriggeredSideEffect = true;
32879
+ });
32880
+ return didFindNavigation && !didFindOtherTriggeredSideEffect;
32881
+ };
32202
32882
  const isEqualityToLiteralGuard = (guardExpression) => {
32203
32883
  const parent = guardExpression.expression.parent;
32204
32884
  if (!isNodeOfType(parent, "BinaryExpression")) return false;
@@ -32263,18 +32943,35 @@ const noEffectEventHandler = defineRule({
32263
32943
  if (statements.length === 0) return;
32264
32944
  const soleStatement = statements[0];
32265
32945
  if (!isNodeOfType(soleStatement, "IfStatement")) return;
32266
- const guardExpressions = [];
32267
- collectGuardExpressions(soleStatement.test, guardExpressions);
32268
- const matchingPropGuardExpressions = guardExpressions.filter((guardExpression) => hasDependencyMatch(guardExpression, dependencyExpressions) && propStackTracker.isPropName(guardExpression.rootIdentifierName, node));
32946
+ const initialGuardExpressions = [];
32947
+ collectGuardExpressions(soleStatement.test, initialGuardExpressions);
32948
+ const guardExpressions = collectLeadingEarlyReturnGuards(statements);
32949
+ if (guardExpressions.length === 0) guardExpressions.push(...initialGuardExpressions);
32950
+ const matchingPropGuardExpressions = initialGuardExpressions.filter((guardExpression) => hasDependencyMatch(guardExpression, dependencyExpressions) && propStackTracker.isPropName(guardExpression.rootIdentifierName, node));
32269
32951
  if (matchingPropGuardExpressions.length === 0) return;
32270
32952
  const isSingleGuardedEventLikeStatement = statements.length === 1 && hasEventLikeNode(soleStatement.consequent);
32271
32953
  const isEarlyReturnGuardedEventLikeBody = statements.length > 1 && !soleStatement.alternate && isReturnOnlyStatement(soleStatement.consequent) && hasEventLikeRemainingStatements(statements.slice(1));
32272
32954
  if (!isSingleGuardedEventLikeStatement && !isEarlyReturnGuardedEventLikeBody) return;
32273
32955
  if (isEarlyReturnGuardedEventLikeBody && !isSingleGuardedEventLikeStatement && matchingPropGuardExpressions.every(isEqualityToLiteralGuard)) return;
32274
- if (guardExpressions.some((guardExpression) => !matchingPropGuardExpressions.some((matchingGuardExpression) => matchingGuardExpression.expression === guardExpression.expression))) {
32956
+ if (initialGuardExpressions.some((guardExpression) => !matchingPropGuardExpressions.some((matchingGuardExpression) => matchingGuardExpression.expression === guardExpression.expression))) {
32275
32957
  const matchingPropRootNames = new Set(matchingPropGuardExpressions.map((guardExpression) => guardExpression.rootIdentifierName));
32276
32958
  if (!(isSingleGuardedEventLikeStatement ? doesEventLikeCallReferenceAnyRoot(soleStatement.consequent, matchingPropRootNames) : doesAnyEventLikeCallReferenceAnyRoot(statements.slice(1), matchingPropRootNames))) return;
32277
32959
  }
32960
+ if (isEarlyReturnGuardedEventLikeBody) {
32961
+ const reconciliationGuardRootNames = new Set(guardExpressions.filter((guardExpression) => !propStackTracker.isPropName(guardExpression.rootIdentifierName, node) && hasAliasedDependencyMatch(guardExpression, dependencyExpressions, context.scopes) && guardExpressions.some((comparisonGuardExpression) => {
32962
+ if (comparisonGuardExpression.rootIdentifierName !== guardExpression.rootIdentifierName) return false;
32963
+ const comparisonExpression = comparisonGuardExpression.expression.parent;
32964
+ if (!isNodeOfType(comparisonExpression, "BinaryExpression") || comparisonExpression.operator !== "===" && comparisonExpression.operator !== "==") return false;
32965
+ const comparedRootIdentifierName = getRootIdentifierName(comparisonExpression.left === comparisonGuardExpression.expression ? comparisonExpression.right : comparisonExpression.left);
32966
+ return Boolean(comparedRootIdentifierName && propStackTracker.isPropName(comparedRootIdentifierName, node));
32967
+ })).map((guardExpression) => guardExpression.rootIdentifierName));
32968
+ if (reconciliationGuardRootNames.size > 0) {
32969
+ const matchingPropRootNames = new Set(matchingPropGuardExpressions.map((guardExpression) => guardExpression.rootIdentifierName));
32970
+ const eventLikeStatements = statements.slice(1);
32971
+ const triggeredStatements = eventLikeStatements.filter(hasEventLikeNode);
32972
+ if (triggeredStatements.length > 0 && triggeredStatements.every((statement) => isReactRouterReplacementNavigation(statement, reconciliationGuardRootNames, context.scopes)) && !doesAnyEventLikeCallReferenceAnyRoot(eventLikeStatements, matchingPropRootNames)) return;
32973
+ }
32974
+ }
32278
32975
  context.report({
32279
32976
  node,
32280
32977
  message: "This useEffect is simulating an event handler, which costs an extra render & runs late."
@@ -32555,7 +33252,7 @@ const isFunctionUsedOutsideHandlers = (analysis, functionNode, componentFunction
32555
33252
  if (isInsideInlineEventHandler(identifier, componentFunction)) return false;
32556
33253
  const parent = identifier.parent;
32557
33254
  if (parent && isNodeOfType(parent, "CallExpression") && parent.callee === identifier) {
32558
- if (findEnclosingFunction(parent) === functionNode) return false;
33255
+ if (findEnclosingFunction$1(parent) === functionNode) return false;
32559
33256
  if (isInsideProvenEventHandler(analysis, parent, componentFunction, false)) return false;
32560
33257
  }
32561
33258
  return true;
@@ -32601,7 +33298,7 @@ const isStateWrittenOnlyFromEventHandlers = (analysis, stateReference) => {
32601
33298
  if (!setterVariable) return false;
32602
33299
  const stateDeclarator = getUseStateDecl(analysis, stateReference);
32603
33300
  if (!stateDeclarator) return false;
32604
- const componentFunction = findEnclosingFunction(stateDeclarator);
33301
+ const componentFunction = findEnclosingFunction$1(stateDeclarator);
32605
33302
  if (!componentFunction) return false;
32606
33303
  let hasWriter = false;
32607
33304
  for (const reference of setterVariable.references) {
@@ -33663,8 +34360,8 @@ const isAwaitedInFunction = (request, functionNode) => {
33663
34360
  return false;
33664
34361
  };
33665
34362
  const isCompletionSinkForRequest = (completionSink, request) => {
33666
- const requestFunction = findEnclosingFunction(request);
33667
- const completionSinkFunction = findEnclosingFunction(completionSink);
34363
+ const requestFunction = findEnclosingFunction$1(request);
34364
+ const completionSinkFunction = findEnclosingFunction$1(completionSink);
33668
34365
  if (!requestFunction || !completionSinkFunction) return false;
33669
34366
  if (requestFunction === completionSinkFunction) {
33670
34367
  if (!isAwaitedInFunction(request, requestFunction)) return false;
@@ -33951,13 +34648,6 @@ const isPublishedLibraryPackage = (filename) => {
33951
34648
  return manifest.private !== true && typeof manifest.peerDependencies === "object" && manifest.peerDependencies !== null && "react" in manifest.peerDependencies;
33952
34649
  };
33953
34650
  //#endregion
33954
- //#region src/plugin/utils/is-type-only-import.ts
33955
- const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
33956
- if (!specifiers || specifiers.length === 0) return false;
33957
- return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
33958
- };
33959
- const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
33960
- //#endregion
33961
34651
  //#region src/plugin/rules/bundle-size/no-full-lodash-import.ts
33962
34652
  const isLibraryDevPage = (filename) => {
33963
34653
  if (!filename) return false;
@@ -34521,7 +35211,7 @@ const isInRenderedOutput = (node, componentOrHookNode, scopes) => {
34521
35211
  return attribute ? !isEventHandlerAttribute(attribute) : true;
34522
35212
  }
34523
35213
  if (isNodeOfType(parentNode, "ReturnStatement")) {
34524
- if (findEnclosingFunction(parentNode) === componentOrHookNode) return true;
35214
+ if (findEnclosingFunction$1(parentNode) === componentOrHookNode) return true;
34525
35215
  }
34526
35216
  if (parentNode === componentOrHookNode) return isFunctionLike$2(componentOrHookNode) && !isNodeOfType(componentOrHookNode.body, "BlockStatement") && componentOrHookNode.body === currentNode;
34527
35217
  if (isFunctionLike$2(parentNode) && !executesDuringRender(parentNode, scopes)) return false;
@@ -34644,7 +35334,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
34644
35334
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
34645
35335
  const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
34646
35336
  if (!componentOrHookNode) return;
34647
- const enclosingFunction = findEnclosingFunction(node);
35337
+ const enclosingFunction = findEnclosingFunction$1(node);
34648
35338
  if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
34649
35339
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
34650
35340
  if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
@@ -35092,10 +35782,10 @@ const isInsideInstanceField = (node) => {
35092
35782
  return false;
35093
35783
  };
35094
35784
  const isOneShotModuleInitialization = (node, context) => {
35095
- let functionNode = findEnclosingFunction(node);
35785
+ let functionNode = findEnclosingFunction$1(node);
35096
35786
  while (functionNode) {
35097
35787
  if (!executesDuringRender(functionNode, context.scopes)) return false;
35098
- functionNode = findEnclosingFunction(functionNode);
35788
+ functionNode = findEnclosingFunction$1(functionNode);
35099
35789
  }
35100
35790
  return !isInsideInstanceField(node);
35101
35791
  };
@@ -36079,10 +36769,10 @@ const getMethodCalls = (functionNode, scopes) => {
36079
36769
  const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, scopes, visitedSymbolIds, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
36080
36770
  if (visitedFunctionNodes.has(functionNode)) return false;
36081
36771
  visitedFunctionNodes.add(functionNode);
36082
- const usageFunction = findEnclosingFunction(usageNode);
36772
+ const usageFunction = findEnclosingFunction$1(usageNode);
36083
36773
  const immediateCall = getDirectCallForExpression(functionNode);
36084
36774
  if (immediateCall) {
36085
- const immediateCallFunction = findEnclosingFunction(immediateCall);
36775
+ const immediateCallFunction = findEnclosingFunction$1(immediateCall);
36086
36776
  if (immediateCallFunction === usageFunction) {
36087
36777
  const immediateCallStart = getRangeStart(immediateCall);
36088
36778
  return immediateCallStart === null || immediateCallStart < usageBoundary;
@@ -36091,7 +36781,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
36091
36781
  return isFunctionInvokedBeforeUsage(immediateCallFunction, usageNode, usageBoundary, scopes, visitedSymbolIds, new Set(visitedFunctionNodes));
36092
36782
  }
36093
36783
  for (const methodCall of getMethodCalls(functionNode, scopes)) {
36094
- const methodCallFunction = findEnclosingFunction(methodCall);
36784
+ const methodCallFunction = findEnclosingFunction$1(methodCall);
36095
36785
  if (methodCallFunction === usageFunction) {
36096
36786
  const methodCallStart = getRangeStart(methodCall);
36097
36787
  if (methodCallStart === null || methodCallStart < usageBoundary) return true;
@@ -36114,7 +36804,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
36114
36804
  if (scopes.symbolFor(child)?.declarationNode !== symbol.declarationNode) return;
36115
36805
  const call = getDirectCallForExpression(child);
36116
36806
  if (!call) return false;
36117
- const callFunction = findEnclosingFunction(call);
36807
+ const callFunction = findEnclosingFunction$1(call);
36118
36808
  if (callFunction === usageFunction) {
36119
36809
  const callStart = getRangeStart(call);
36120
36810
  wasInvokedBeforeUsage = callStart === null || callStart < usageBoundary;
@@ -36145,14 +36835,14 @@ const wasMutatedBeforeUsage = (symbol, usageNode, readNode, scopes, visitedMutat
36145
36835
  visitedMutationSymbolIds.add(symbol.id);
36146
36836
  const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, readNode) : inheritedUsageBoundary;
36147
36837
  if (typeof usageBoundary !== "number") return true;
36148
- const usageFunction = findEnclosingFunction(usageNode);
36838
+ const usageFunction = findEnclosingFunction$1(usageNode);
36149
36839
  return symbol.references.some((reference) => {
36150
36840
  const referenceStart = getRangeStart(reference.identifier);
36151
36841
  const simpleAlias = getSimpleAlias(reference.identifier, scopes);
36152
36842
  if (simpleAlias) return wasMutatedBeforeUsage(simpleAlias.symbol, usageNode, readNodesBySymbolId.get(simpleAlias.symbol.id) ?? simpleAlias.readNode, scopes, new Set(visitedMutationSymbolIds), usageBoundary, readNodesBySymbolId);
36153
36843
  if (!isPotentialMutationReference(reference.identifier, readNode)) return false;
36154
36844
  if (referenceStart === null) return true;
36155
- const mutationFunction = findEnclosingFunction(reference.identifier);
36845
+ const mutationFunction = findEnclosingFunction$1(reference.identifier);
36156
36846
  if (mutationFunction === usageFunction) return referenceStart < usageBoundary;
36157
36847
  if (!mutationFunction) return usageFunction !== null;
36158
36848
  if (usageFunction && isAstDescendant(usageFunction, mutationFunction)) return true;
@@ -36734,13 +37424,6 @@ const isReactNamespaceExpression = (node, scopes) => {
36734
37424
  return isReactNamespaceImport(node, scopes);
36735
37425
  };
36736
37426
  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));
36737
- const getDestructuredPropertyName$1 = (symbol) => {
36738
- const property = symbol.bindingIdentifier.parent;
36739
- if (!property || !isNodeOfType(property, "Property") || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
36740
- if (isNodeOfType(property.key, "Identifier") && !property.computed) return property.key.name;
36741
- if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
36742
- return null;
36743
- };
36744
37427
  const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
36745
37428
  if (visitedSymbolIds.has(symbol.id)) return false;
36746
37429
  visitedSymbolIds.add(symbol.id);
@@ -36750,7 +37433,7 @@ const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
36750
37433
  }
36751
37434
  const init = symbol.initializer;
36752
37435
  if (!init) return false;
36753
- const destructuredPropertyName = getDestructuredPropertyName$1(symbol);
37436
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
36754
37437
  if (destructuredPropertyName && REACT_HOC_NAMES.has(destructuredPropertyName) && isReactNamespaceExpression(init, scopes)) return true;
36755
37438
  if (isReactHocMemberReference(init, scopes)) return true;
36756
37439
  if (isNodeOfType(init, "Identifier")) {
@@ -38504,12 +39187,6 @@ const getDeclarationKind = (declarator) => {
38504
39187
  return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
38505
39188
  };
38506
39189
  const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
38507
- const getDestructuredPropertyName = (bindingIdentifier) => {
38508
- const property = bindingIdentifier.parent;
38509
- if (!property || !isNodeOfType(property, "Property")) return null;
38510
- if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
38511
- return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
38512
- };
38513
39190
  const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
38514
39191
  const unwrappedExpression = stripParenExpression(expression);
38515
39192
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -38519,7 +39196,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
38519
39196
  if (isProp(analysis, callbackReference)) {
38520
39197
  if (isWholePropsObjectReference(analysis, callbackReference)) return null;
38521
39198
  const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
38522
- return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
39199
+ return (bindingIdentifier && getDestructuredBindingPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
38523
39200
  }
38524
39201
  if (hasMutableBindingWrite(callbackReference)) return null;
38525
39202
  const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
@@ -38529,7 +39206,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
38529
39206
  visitedVariables.add(callbackVariable);
38530
39207
  if (isNodeOfType(declarator.id, "ObjectPattern")) {
38531
39208
  const bindingIdentifier = callbackVariable.defs[0]?.name;
38532
- const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
39209
+ const propertyName = bindingIdentifier ? getDestructuredBindingPropertyName(bindingIdentifier) : null;
38533
39210
  const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
38534
39211
  return propertyName && propsReference ? propertyName : null;
38535
39212
  }
@@ -38632,7 +39309,7 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
38632
39309
  const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
38633
39310
  if (!bindingProvenance) return null;
38634
39311
  const { refCall, variables } = bindingProvenance;
38635
- const componentFunction = findEnclosingFunction(effectCall);
39312
+ const componentFunction = findEnclosingFunction$1(effectCall);
38636
39313
  if (!componentFunction || !isFunctionLike$2(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
38637
39314
  if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
38638
39315
  const callbackPropNames = /* @__PURE__ */ new Set();
@@ -39088,14 +39765,25 @@ const isPropsChildrenLength = (node, scopes) => {
39088
39765
  const unwrappedNode = stripParenExpression(node);
39089
39766
  return isNodeOfType(unwrappedNode, "MemberExpression") && !unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Identifier") && unwrappedNode.property.name === "length" && resolvesToPropsChildren(stripParenExpression(unwrappedNode.object), scopes);
39090
39767
  };
39768
+ const resolveStaticNumericValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39769
+ const unwrappedNode = stripParenExpression(node);
39770
+ if (isNodeOfType(unwrappedNode, "Literal") && typeof unwrappedNode.value === "number") return Number.isFinite(unwrappedNode.value) ? unwrappedNode.value : null;
39771
+ if (!isNodeOfType(unwrappedNode, "Identifier")) return null;
39772
+ const symbol = scopes.symbolFor(unwrappedNode);
39773
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
39774
+ const initializer = getDirectConstInitializer(symbol);
39775
+ if (!initializer) return null;
39776
+ return resolveStaticNumericValue(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id));
39777
+ };
39091
39778
  const isLargeTextLengthComparison = (node, scopes) => {
39092
39779
  const unwrappedNode = stripParenExpression(node);
39093
39780
  if (!isNodeOfType(unwrappedNode, "BinaryExpression")) return false;
39094
39781
  const leftIsLength = isPropsChildrenLength(unwrappedNode.left, scopes);
39095
39782
  const rightIsLength = isPropsChildrenLength(unwrappedNode.right, scopes);
39096
39783
  const thresholdNode = leftIsLength ? unwrappedNode.right : unwrappedNode.left;
39097
- if (!leftIsLength && !rightIsLength || !isNodeOfType(thresholdNode, "Literal")) return false;
39098
- if (typeof thresholdNode.value !== "number" || thresholdNode.value < 1e3) return false;
39784
+ if (!leftIsLength && !rightIsLength) return false;
39785
+ const thresholdValue = resolveStaticNumericValue(thresholdNode, scopes);
39786
+ if (thresholdValue === null || thresholdValue < 1e3) return false;
39099
39787
  return leftIsLength ? unwrappedNode.operator === ">" || unwrappedNode.operator === ">=" : unwrappedNode.operator === "<" || unwrappedNode.operator === "<=";
39100
39788
  };
39101
39789
  const isLargeStringOptimizationGuard = (comparison, scopes) => {
@@ -39620,7 +40308,7 @@ const isModuleScopedBinding = (identifier) => {
39620
40308
  const binding = findVariableInitializer(identifier, identifier.name);
39621
40309
  if (!binding) return false;
39622
40310
  if (isNodeOfType(binding.scopeOwner, "Program")) return true;
39623
- if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction(binding.scopeOwner) === null;
40311
+ if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction$1(binding.scopeOwner) === null;
39624
40312
  return false;
39625
40313
  };
39626
40314
  const looksLikeFreshUpdateExpression = (expression) => {
@@ -41537,9 +42225,9 @@ const isReturnedFromAnyEffectInScope = (functionNode, ownerScope) => {
41537
42225
  return isReturnedFromEffect;
41538
42226
  };
41539
42227
  const isInsideEffectCleanupReturn = (node, ownerScope) => {
41540
- let functionNode = findEnclosingFunction(node);
42228
+ let functionNode = findEnclosingFunction$1(node);
41541
42229
  while (functionNode) {
41542
- const outerFunction = findEnclosingFunction(functionNode);
42230
+ const outerFunction = findEnclosingFunction$1(functionNode);
41543
42231
  if (outerFunction && isEffectCallbackFunction(outerFunction) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
41544
42232
  if (isReturnedFromAnyEffectInScope(functionNode, ownerScope)) return true;
41545
42233
  functionNode = outerFunction;
@@ -41547,7 +42235,7 @@ const isInsideEffectCleanupReturn = (node, ownerScope) => {
41547
42235
  return false;
41548
42236
  };
41549
42237
  const hasRefCurrentReassignmentAfterClear = (clearCall, refName) => {
41550
- const enclosingFunction = findEnclosingFunction(clearCall);
42238
+ const enclosingFunction = findEnclosingFunction$1(clearCall);
41551
42239
  if (!isFunctionLike$2(enclosingFunction)) return false;
41552
42240
  if (!isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
41553
42241
  const clearStart = getRangeStart(clearCall);
@@ -42377,7 +43065,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
42377
43065
  return false;
42378
43066
  };
42379
43067
  const isAfterAvailabilityEarlyExit = (node, componentOrHookNode, browserGlobalName, context) => {
42380
- const enclosingFunction = findEnclosingFunction(node);
43068
+ const enclosingFunction = findEnclosingFunction$1(node);
42381
43069
  if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$2(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
42382
43070
  let currentNode = node;
42383
43071
  while (currentNode !== enclosingFunction) {
@@ -43644,7 +44332,11 @@ const resolveSettings$8 = (settings) => {
43644
44332
  propNamePattern: ruleSettings.propNamePattern ?? "render*"
43645
44333
  };
43646
44334
  };
43647
- const expressionContainsJsxOrCreateElement = (root) => {
44335
+ const isReactCreateElementCall = (node, scopes) => isReactApiCall(node, "createElement", scopes, {
44336
+ allowGlobalReactNamespace: true,
44337
+ resolveNamedAliases: true
44338
+ });
44339
+ const expressionContainsJsxOrCreateElement = (root, scopes) => {
43648
44340
  let found = false;
43649
44341
  walkAst(root, (node) => {
43650
44342
  if (found) return false;
@@ -43653,17 +44345,17 @@ const expressionContainsJsxOrCreateElement = (root) => {
43653
44345
  found = true;
43654
44346
  return false;
43655
44347
  }
43656
- if (isNodeOfType(node, "CallExpression") && isCreateElementCall(node)) {
44348
+ if (isNodeOfType(node, "CallExpression") && isReactCreateElementCall(node, scopes)) {
43657
44349
  found = true;
43658
44350
  return false;
43659
44351
  }
43660
44352
  });
43661
44353
  return found;
43662
44354
  };
43663
- const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement, controlFlow);
43664
- const isReactClassComponent = (classNode) => {
44355
+ const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => expressionContainsJsxOrCreateElement(expression, scopes), controlFlow);
44356
+ const isReactClassComponent = (classNode, scopes) => {
43665
44357
  if (isEs6Component(classNode)) return true;
43666
- return expressionContainsJsxOrCreateElement(classNode);
44358
+ return expressionContainsJsxOrCreateElement(classNode, scopes);
43667
44359
  };
43668
44360
  const findEnclosingComponent = (node, scopes, controlFlow) => {
43669
44361
  let walker = node.parent;
@@ -43680,7 +44372,7 @@ const findEnclosingComponent = (node, scopes, controlFlow) => {
43680
44372
  };
43681
44373
  }
43682
44374
  if (isNodeOfType(walker, "ClassDeclaration") || isNodeOfType(walker, "ClassExpression")) {
43683
- if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker)) return {
44375
+ if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker, scopes)) return {
43684
44376
  component: walker,
43685
44377
  name: walker.id.name
43686
44378
  };
@@ -43769,12 +44461,12 @@ const isObjectCallbackCandidate = (node) => {
43769
44461
  }
43770
44462
  return false;
43771
44463
  };
43772
- const hocCallContainsComponent = (call) => {
44464
+ const hocCallContainsComponent = (call, scopes) => {
43773
44465
  const firstArgument = call.arguments[0];
43774
44466
  if (!firstArgument) return false;
43775
- if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument);
43776
- if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument);
43777
- return expressionContainsJsxOrCreateElement(firstArgument);
44467
+ if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument, scopes);
44468
+ if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument, scopes);
44469
+ return expressionContainsJsxOrCreateElement(firstArgument, scopes);
43778
44470
  };
43779
44471
  const isFirstArgumentOfHocCall = (node) => {
43780
44472
  const parent = node.parent;
@@ -43807,19 +44499,35 @@ const isReturnOfMapCallback = (node) => {
43807
44499
  }
43808
44500
  return false;
43809
44501
  };
43810
- const isCloneElementCall = (node) => {
43811
- if (!isNodeOfType(node, "CallExpression")) return false;
43812
- const callee = node.callee;
43813
- if (isNodeOfType(callee, "Identifier")) return callee.name === "cloneElement";
43814
- return isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "cloneElement";
43815
- };
43816
44502
  const TS_VALUE_PASSTHROUGH_TYPES = new Set([
43817
44503
  "TSAsExpression",
43818
44504
  "TSNonNullExpression",
43819
44505
  "TSSatisfiesExpression",
43820
44506
  "TSTypeAssertion"
43821
44507
  ]);
43822
- const isRenderFlowingReadReference = (identifier) => {
44508
+ const ELEMENT_TYPE_PROP_NAMES = new Set([
44509
+ "as",
44510
+ "body",
44511
+ "calendarcontainer",
44512
+ "component",
44513
+ "fallback",
44514
+ "tooltip"
44515
+ ]);
44516
+ const isElementTypeJsxAttribute = (node) => {
44517
+ if (!isNodeOfType(node, "JSXAttribute")) return false;
44518
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return false;
44519
+ const attributeName = node.name.name;
44520
+ return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
44521
+ };
44522
+ const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
44523
+ allowGlobalReactNamespace: true,
44524
+ resolveNamedAliases: true
44525
+ });
44526
+ const isReactLazyCall = (call, scopes) => isReactApiCall(call, "lazy", scopes, {
44527
+ allowGlobalReactNamespace: true,
44528
+ resolveNamedAliases: true
44529
+ });
44530
+ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
43823
44531
  let valueNode = identifier;
43824
44532
  let parent = valueNode.parent;
43825
44533
  while (parent) {
@@ -43829,20 +44537,26 @@ const isRenderFlowingReadReference = (identifier) => {
43829
44537
  continue;
43830
44538
  }
43831
44539
  switch (parent.type) {
43832
- case "JSXExpressionContainer":
43833
- case "ReturnStatement": return true;
43834
- case "ArrowFunctionExpression": return parent.body === valueNode;
44540
+ case "JSXOpeningElement": return parent.name === valueNode;
44541
+ case "JSXExpressionContainer": return Boolean(parent.parent && isElementTypeJsxAttribute(parent.parent));
44542
+ case "ReturnStatement": return false;
44543
+ case "ArrowFunctionExpression": return false;
43835
44544
  case "CallExpression":
43836
44545
  if (parent.callee === valueNode) return false;
43837
- if (isCreateElementCall(parent) || isCloneElementCall(parent)) return true;
44546
+ if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
44547
+ if (isReactCreateElementCall(parent, scopes)) return true;
43838
44548
  valueNode = parent;
43839
44549
  parent = parent.parent;
43840
44550
  continue;
43841
- case "VariableDeclarator": return parent.init === valueNode && isNodeOfType(parent.id, "Identifier") && isReactComponentName(parent.id.name);
43842
- case "AssignmentExpression": {
43843
- const assignmentTarget = parent.left;
43844
- return parent.right === valueNode && isNodeOfType(assignmentTarget, "Identifier") && isReactComponentName(assignmentTarget.name);
44551
+ case "VariableDeclarator": {
44552
+ if (parent.init !== valueNode || !isNodeOfType(parent.id, "Identifier")) return false;
44553
+ const aliasSymbol = scopes.symbolFor(parent.id);
44554
+ if (!aliasSymbol || aliasSymbol.kind !== "const" || visitedSymbols.has(aliasSymbol.id)) return false;
44555
+ const nextVisitedSymbols = new Set(visitedSymbols);
44556
+ nextVisitedSymbols.add(aliasSymbol.id);
44557
+ return aliasSymbol.references.some((reference) => reference.flag === "read" && isRenderFlowingReadReference(reference.identifier, scopes, nextVisitedSymbols));
43845
44558
  }
44559
+ case "AssignmentExpression": return false;
43846
44560
  case "Property":
43847
44561
  if (parent.value !== valueNode) return false;
43848
44562
  valueNode = parent;
@@ -43880,6 +44594,7 @@ const noUnstableNestedComponents = defineRule({
43880
44594
  severity: "warn",
43881
44595
  recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
43882
44596
  category: "Performance",
44597
+ tags: ["react-jsx-only"],
43883
44598
  create: (context) => {
43884
44599
  const settings = resolveSettings$8(context.settings);
43885
44600
  const renderPropRegex = compileGlob(settings.propNamePattern);
@@ -43942,7 +44657,7 @@ const noUnstableNestedComponents = defineRule({
43942
44657
  },
43943
44658
  Identifier(node) {
43944
44659
  if (!isReactComponentName(node.name)) return;
43945
- if (!isRenderFlowingReadReference(node)) return;
44660
+ if (!isRenderFlowingReadReference(node, context.scopes)) return;
43946
44661
  recordInstantiation(node, node.name);
43947
44662
  },
43948
44663
  FunctionDeclaration: checkFunctionLike,
@@ -43951,28 +44666,33 @@ const noUnstableNestedComponents = defineRule({
43951
44666
  ClassDeclaration(node) {
43952
44667
  if (!node.id) return;
43953
44668
  if (!isReactComponentName(node.id.name)) return;
43954
- if (!isReactClassComponent(node)) return;
44669
+ if (!isReactClassComponent(node, context.scopes)) return;
43955
44670
  enqueueCandidate(node, null);
43956
44671
  },
43957
44672
  ClassExpression(node) {
43958
44673
  const inferredName = node.id?.name ?? inferFunctionLikeName(node);
43959
44674
  if (!inferredName || !isReactComponentName(inferredName)) return;
43960
- if (!isReactClassComponent(node)) return;
44675
+ if (!isReactClassComponent(node, context.scopes)) return;
43961
44676
  enqueueCandidate(node, null);
43962
44677
  },
43963
44678
  CallExpression(node) {
43964
- if (isCreateElementCall(node)) {
44679
+ if (isReactCreateElementCall(node, context.scopes)) {
43965
44680
  const firstArgument = node.arguments[0];
43966
44681
  if (firstArgument && isNodeOfType(firstArgument, "Identifier")) recordInstantiation(firstArgument, firstArgument.name);
43967
44682
  else if (firstArgument && isNodeOfType(firstArgument, "MemberExpression")) recordMemberChainInstantiation(firstArgument);
43968
44683
  }
43969
- if (!isHocCallee$1(node)) return;
43970
- if (!hocCallContainsComponent(node)) return;
43971
- enqueueCandidate(node, null);
44684
+ const isReactLazy = isReactLazyCall(node, context.scopes);
44685
+ if (!isReactLazy && !isHocCallee$1(node)) return;
44686
+ if (!isReactLazy && !hocCallContainsComponent(node, context.scopes)) return;
44687
+ const inferredName = inferFunctionLikeName(node);
44688
+ const propInfo = isComponentDeclaredInProp(node);
44689
+ if (propInfo === null && (!inferredName || !isReactComponentName(inferredName))) return;
44690
+ enqueueCandidate(node, propInfo === null ? inferredName : null);
43972
44691
  },
43973
44692
  "Program:exit"() {
43974
44693
  for (const report of queuedReports) {
43975
44694
  if (report.requiredInstantiationName !== null) {
44695
+ if ((report.requiredInstantiationBinding ? context.scopes.symbolFor(report.requiredInstantiationBinding) : null)?.references.some((reference) => reference.flag === "write" || reference.flag === "read-write")) continue;
43976
44696
  if (!(report.requiredInstantiationBinding !== null ? instantiatedBindingIdentifiers.has(report.requiredInstantiationBinding) : instantiatedComponentNames.has(report.requiredInstantiationName))) continue;
43977
44697
  }
43978
44698
  context.report({
@@ -45005,7 +45725,7 @@ const findChildrenDestructuringFunction = (identifier, scopes) => {
45005
45725
  const symbol = scopes.symbolFor(identifier);
45006
45726
  if (symbol) {
45007
45727
  if (symbol.kind !== "parameter") return null;
45008
- const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45728
+ const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
45009
45729
  if (declaringFunction && destructuresChildrenAsFirstParam(declaringFunction)) return declaringFunction;
45010
45730
  return null;
45011
45731
  }
@@ -45051,7 +45771,7 @@ const isChildrenMemberExpression = (node, scopes) => {
45051
45771
  const symbol = scopes.symbolFor(propsObject);
45052
45772
  if (symbol) {
45053
45773
  if (symbol.kind !== "parameter") return false;
45054
- const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45774
+ const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
45055
45775
  return declaringFunction ? isDeclaringFunctionComponentLike(declaringFunction) : false;
45056
45776
  }
45057
45777
  return hasComponentLikeAncestorFunction(node);
@@ -45703,7 +46423,7 @@ const doesFunctionReturnsObjectLiteral = (functionNode) => {
45703
46423
  //#endregion
45704
46424
  //#region src/plugin/utils/enclosing-component-or-hook-scope.ts
45705
46425
  const enclosingComponentOrHookScope = (startNode, ownScopeFor) => {
45706
- const functionNode = findEnclosingFunction(startNode);
46426
+ const functionNode = findEnclosingFunction$1(startNode);
45707
46427
  if (!functionNode) return null;
45708
46428
  const displayName = componentOrHookDisplayNameForFunction(functionNode);
45709
46429
  if (!displayName) return null;
@@ -46765,7 +47485,7 @@ const isTanstackQuerySource = (source) => TANSTACK_QUERY_PACKAGE_PATTERN.test(so
46765
47485
  //#region src/plugin/rules/tanstack-query/query-destructure-result.ts
46766
47486
  const isHookReturnForwardingSpread = (objectExpression) => {
46767
47487
  const objectParent = objectExpression.parent;
46768
- const enclosingFunction = findEnclosingFunction(objectExpression);
47488
+ const enclosingFunction = findEnclosingFunction$1(objectExpression);
46769
47489
  if (!enclosingFunction) return false;
46770
47490
  if (!(isNodeOfType(objectParent, "ReturnStatement") || isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && enclosingFunction.body === objectExpression)) return false;
46771
47491
  const enclosingName = componentOrHookDisplayNameForFunction(enclosingFunction);
@@ -47143,10 +47863,10 @@ const hasSuspensionBefore = (functionNode, boundary, context) => {
47143
47863
  return hasSuspension;
47144
47864
  };
47145
47865
  const isFunctionAncestor = (ancestor, functionNode) => {
47146
- let enclosingFunction = findEnclosingFunction(functionNode);
47866
+ let enclosingFunction = findEnclosingFunction$1(functionNode);
47147
47867
  while (enclosingFunction) {
47148
47868
  if (enclosingFunction === ancestor) return true;
47149
- enclosingFunction = findEnclosingFunction(enclosingFunction);
47869
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47150
47870
  }
47151
47871
  return false;
47152
47872
  };
@@ -47168,7 +47888,7 @@ const functionInvokesTarget = (callerFunction, targetFunction, context, visitedF
47168
47888
  return invokesTarget;
47169
47889
  };
47170
47890
  const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
47171
- const boundaryFunction = findEnclosingFunction(boundary);
47891
+ const boundaryFunction = findEnclosingFunction$1(boundary);
47172
47892
  const boundaryStart = getRangeStart(boundary);
47173
47893
  if (!boundaryFunction || boundaryStart === null) return false;
47174
47894
  let isInvokedBefore = false;
@@ -47205,11 +47925,11 @@ const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, conte
47205
47925
  const isWriteExecutedBefore = (writeNode, boundary, context, deferredExecutionFunction) => {
47206
47926
  const writeStart = getRangeStart(writeNode);
47207
47927
  const boundaryStart = getRangeStart(boundary);
47208
- const writeFunction = findEnclosingFunction(writeNode);
47928
+ const writeFunction = findEnclosingFunction$1(writeNode);
47209
47929
  const writeExecutionBoundary = writeFunction ?? findProgramRoot(writeNode);
47210
47930
  if (writeStart === null || boundaryStart === null || !writeExecutionBoundary || !isNodeReachableWithinFunction(writeNode, context) || !isUnconditionallyExecuted(writeNode, writeExecutionBoundary, context)) return false;
47211
- const boundaryFunction = findEnclosingFunction(boundary);
47212
- const renderFunction = deferredExecutionFunction ? findEnclosingFunction(deferredExecutionFunction) : null;
47931
+ const boundaryFunction = findEnclosingFunction$1(boundary);
47932
+ const renderFunction = deferredExecutionFunction ? findEnclosingFunction$1(deferredExecutionFunction) : null;
47213
47933
  if (writeFunction === boundaryFunction) return writeStart < boundaryStart;
47214
47934
  if (!writeFunction) return writeStart < boundaryStart;
47215
47935
  if (boundaryFunction && isFunctionAncestor(writeFunction, boundaryFunction)) {
@@ -47481,8 +48201,8 @@ const queryStableQueryClient = defineRule({
47481
48201
  create: (context) => ({ NewExpression(node) {
47482
48202
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "QueryClient") return;
47483
48203
  if (isStableHookWrapperArgument(node)) return;
47484
- let enclosingFunction = findEnclosingFunction(node);
47485
- while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
48204
+ let enclosingFunction = findEnclosingFunction$1(node);
48205
+ while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47486
48206
  if (!enclosingFunction) return;
47487
48207
  if (!isComponentFunction(enclosingFunction)) return;
47488
48208
  context.report({
@@ -47577,7 +48297,7 @@ const reactCompilerNoManualMemoization = defineRule({
47577
48297
  const comparatorArgument = node.arguments?.[1];
47578
48298
  if (comparatorArgument && !isNullishComparatorArgument(comparatorArgument)) return;
47579
48299
  } else {
47580
- const enclosingFunction = findEnclosingFunction(node);
48300
+ const enclosingFunction = findEnclosingFunction$1(node);
47581
48301
  if (!enclosingFunction || !isCompilerInferableFunction(enclosingFunction)) return;
47582
48302
  }
47583
48303
  const removalMessage = REMOVAL_MESSAGE_BY_REACT_API_NAME.get(apiName);
@@ -50682,7 +51402,7 @@ const isReactNativeDimensionsCallee = (node, callee) => {
50682
51402
  };
50683
51403
  const STYLE_FACTORY_CALLEE_PATTERN$1 = /(?:^|\.)(?:make|create)(?:Use)?Styles$/;
50684
51404
  const isInsideStyleFactoryCallback = (node) => {
50685
- const enclosingFunction = findEnclosingFunction(node);
51405
+ const enclosingFunction = findEnclosingFunction$1(node);
50686
51406
  if (!enclosingFunction) return false;
50687
51407
  const callExpression = enclosingFunction.parent;
50688
51408
  if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) return false;
@@ -57538,12 +58258,14 @@ const declarationAwaitsRequestScopedCall = (declaration) => {
57538
58258
  }
57539
58259
  return false;
57540
58260
  };
57541
- const declarationAwaitsGate = (declaration) => {
58261
+ const declarationAwaitsGate = (declaration, context) => {
57542
58262
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
57543
58263
  for (const declarator of declaration.declarations ?? []) {
57544
58264
  if (!isNodeOfType(declarator.init, "AwaitExpression")) continue;
57545
58265
  const argument = declarator.init.argument;
57546
58266
  if (!isNodeOfType(argument, "CallExpression")) continue;
58267
+ if (hasPossibleStaticMemberCallWrite(argument, context.scopes)) return true;
58268
+ if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) continue;
57547
58269
  const calleeName = getCalleeName$2(argument);
57548
58270
  if (!calleeName) continue;
57549
58271
  if (isAuthGuardName(calleeName)) return true;
@@ -57571,7 +58293,7 @@ const serverSequentialIndependentAwait = defineRule({
57571
58293
  if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
57572
58294
  if (declarationAwaitsExistingPromise(nextStatement)) continue;
57573
58295
  if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
57574
- if (declarationAwaitsGate(currentStatement)) continue;
58296
+ if (declarationAwaitsGate(currentStatement, context)) continue;
57575
58297
  context.report({
57576
58298
  node: nextStatement,
57577
58299
  message: "This await doesn't use the previous result, so your users wait twice as long for nothing."
@@ -57661,6 +58383,26 @@ const stateInConstructor = defineRule({
57661
58383
  }
57662
58384
  });
57663
58385
  //#endregion
58386
+ //#region src/plugin/utils/collect-jsx-runtime-imports.ts
58387
+ const REACT_RUNTIME_PACKAGE_PREFIXES = ["react", "react-dom"];
58388
+ const matchesPackage = (source, packageName) => source === packageName || source.startsWith(`${packageName}/`);
58389
+ const collectJsxRuntimeImports = (program) => {
58390
+ let hasReactRuntime = false;
58391
+ let hasSolidRuntime = false;
58392
+ for (const statement of program.body) {
58393
+ if (!isNodeOfType(statement, "ImportDeclaration")) continue;
58394
+ if (isTypeOnlyImport(statement)) continue;
58395
+ const source = statement.source.value;
58396
+ if (typeof source !== "string") continue;
58397
+ if (matchesPackage(source, "solid-js")) hasSolidRuntime = true;
58398
+ if (REACT_RUNTIME_PACKAGE_PREFIXES.some((packageName) => matchesPackage(source, packageName))) hasReactRuntime = true;
58399
+ }
58400
+ return {
58401
+ hasReactRuntime,
58402
+ hasSolidRuntime
58403
+ };
58404
+ };
58405
+ //#endregion
57664
58406
  //#region src/plugin/rules/react-builtins/style-prop-object.ts
57665
58407
  const MESSAGE$1 = "Your styles don't render because you passed the `style` prop a string instead of an object.";
57666
58408
  const resolveSettings = (settings) => {
@@ -57722,6 +58464,13 @@ const isInvalidStyleExpression = (expression) => {
57722
58464
  }
57723
58465
  return false;
57724
58466
  };
58467
+ const hasObjectValuedClassList = (openingElement) => openingElement.attributes.some((attribute) => {
58468
+ if (!isNodeOfType(attribute, "JSXAttribute")) return false;
58469
+ if (!isNodeOfType(attribute.name, "JSXIdentifier")) return false;
58470
+ if (attribute.name.name !== "classList") return false;
58471
+ if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return false;
58472
+ return isNodeOfType(attribute.value.expression, "ObjectExpression");
58473
+ });
57725
58474
  const stylePropObject = defineRule({
57726
58475
  id: "style-prop-object",
57727
58476
  title: "Style prop is not an object",
@@ -57731,8 +58480,21 @@ const stylePropObject = defineRule({
57731
58480
  create: (context) => {
57732
58481
  const { allow } = resolveSettings(context.settings);
57733
58482
  const allowSet = new Set(allow);
58483
+ let fileIsProvenSolidJsx = false;
57734
58484
  return {
58485
+ Program: (node) => {
58486
+ const runtimeImports = collectJsxRuntimeImports(node);
58487
+ let hasSolidSyntaxMarker = false;
58488
+ if (!runtimeImports.hasReactRuntime && !runtimeImports.hasSolidRuntime) walkAst(node, (descendantNode) => {
58489
+ if (isNodeOfType(descendantNode, "JSXOpeningElement") && hasObjectValuedClassList(descendantNode)) {
58490
+ hasSolidSyntaxMarker = true;
58491
+ return false;
58492
+ }
58493
+ });
58494
+ fileIsProvenSolidJsx = !runtimeImports.hasReactRuntime && (runtimeImports.hasSolidRuntime || hasSolidSyntaxMarker);
58495
+ },
57735
58496
  JSXOpeningElement(node) {
58497
+ if (fileIsProvenSolidJsx) return;
57736
58498
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
57737
58499
  const elementName = resolveJsxElementType(node);
57738
58500
  if (elementName && allowSet.has(elementName)) return;
@@ -57763,6 +58525,7 @@ const stylePropObject = defineRule({
57763
58525
  }
57764
58526
  },
57765
58527
  CallExpression(node) {
58528
+ if (fileIsProvenSolidJsx) return;
57766
58529
  if (!isCreateElementCall(node)) return;
57767
58530
  const firstArgument = node.arguments[0];
57768
58531
  if (!firstArgument) return;
@@ -58557,7 +59320,7 @@ const tanstackStartNoNavigateInRender = defineRule({
58557
59320
  const isReturnedFromCustomHook = (functionNode) => {
58558
59321
  const parent = functionNode.parent;
58559
59322
  if (isNodeOfType(parent, "ReturnStatement")) {
58560
- const outerFunction = findEnclosingFunction(parent);
59323
+ const outerFunction = findEnclosingFunction$1(parent);
58561
59324
  const hookName = outerFunction ? getFunctionBindingName$1(outerFunction) : null;
58562
59325
  return Boolean(hookName && HOOK_NAME_PATTERN$3.test(hookName));
58563
59326
  }
@@ -58574,13 +59337,13 @@ const tanstackStartNoNavigateInRender = defineRule({
58574
59337
  return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
58575
59338
  };
58576
59339
  const isDeferredNavigateCall = (callNode) => {
58577
- let enclosingFunction = findEnclosingFunction(callNode);
59340
+ let enclosingFunction = findEnclosingFunction$1(callNode);
58578
59341
  while (enclosingFunction) {
58579
59342
  if (isDeferredCallbackPosition(enclosingFunction)) return true;
58580
59343
  if (isWiredAsEventHandler(enclosingFunction)) return true;
58581
59344
  if (isReturnedFromCustomHook(enclosingFunction)) return true;
58582
59345
  if (!isSynchronouslyInvokedAnonymousWrapper(enclosingFunction)) return false;
58583
- enclosingFunction = findEnclosingFunction(enclosingFunction);
59346
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
58584
59347
  }
58585
59348
  return false;
58586
59349
  };