oxlint-plugin-react-doctor 0.7.7-dev.71e6da6 → 0.7.7-dev.79abd71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +46 -0
  2. package/dist/index.js +1519 -686
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,6 +7,13 @@ import { analyze } from "eslint-scope";
7
7
  //#region src/plugin/utils/is-node-of-type.ts
8
8
  const isNodeOfType = (node, type) => node !== null && typeof node === "object" && "type" in node && node.type === type;
9
9
  //#endregion
10
+ //#region src/plugin/utils/is-type-only-import.ts
11
+ const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
12
+ if (!specifiers || specifiers.length === 0) return false;
13
+ return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
14
+ };
15
+ const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
16
+ //#endregion
10
17
  //#region src/plugin/utils/non-react-jsx-dialect.ts
11
18
  const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
12
19
  "solid-js",
@@ -21,17 +28,33 @@ const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
21
28
  "vidode"
22
29
  ]);
23
30
  const NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES = ["solid-js", "@builder.io/qwik"];
31
+ const REACT_JSX_DIALECT_PACKAGE_PREFIXES = [
32
+ "react",
33
+ "react-dom",
34
+ "preact"
35
+ ];
24
36
  const startsWithAny = (source, prefixes) => prefixes.some((prefix) => source === prefix || source.startsWith(`${prefix}/`));
25
- const fileImportsNonReactJsxDialect = (program) => {
37
+ const collectJsxRuntimeImports = (program) => {
38
+ let hasNonReactRuntime = false;
39
+ let hasReactRuntime = false;
26
40
  for (const statement of program.body) {
27
41
  if (!isNodeOfType(statement, "ImportDeclaration")) continue;
28
- const source = statement.source;
42
+ const importDeclaration = statement;
43
+ if (isTypeOnlyImport(importDeclaration)) continue;
44
+ const source = importDeclaration.source;
29
45
  const value = source && typeof source.value === "string" ? source.value : null;
30
46
  if (!value) continue;
31
- if (NON_REACT_JSX_DIALECT_PACKAGES.has(value)) return true;
32
- if (startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) return true;
47
+ if (NON_REACT_JSX_DIALECT_PACKAGES.has(value) || startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) hasNonReactRuntime = true;
48
+ if (startsWithAny(value, REACT_JSX_DIALECT_PACKAGE_PREFIXES)) hasReactRuntime = true;
33
49
  }
34
- return false;
50
+ return {
51
+ hasNonReactRuntime,
52
+ hasReactRuntime
53
+ };
54
+ };
55
+ const fileImportsNonReactJsxDialect = (program) => {
56
+ const runtimeImports = collectJsxRuntimeImports(program);
57
+ return runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
35
58
  };
36
59
  const jsxAttributeIsNonReactDialectMarker = (openingNode) => {
37
60
  for (const attribute of openingNode.attributes) {
@@ -244,6 +267,7 @@ const VISITOR_NODE_NAME_PATTERN = /^[A-Z]/;
244
267
  const wrapCreateForReactJsxOnly = (create) => ((context) => {
245
268
  const innerVisitors = create(context);
246
269
  let fileIsNonReactJsx = false;
270
+ let fileImportsReactRuntime = false;
247
271
  const wrappedVisitors = {};
248
272
  for (const [key, visitor] of Object.entries(innerVisitors)) {
249
273
  if (typeof visitor !== "function") {
@@ -256,14 +280,16 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
256
280
  }
257
281
  if (key === "Program") {
258
282
  wrappedVisitors.Program = (node) => {
259
- fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
283
+ const runtimeImports = collectJsxRuntimeImports(node);
284
+ fileImportsReactRuntime = runtimeImports.hasReactRuntime;
285
+ fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
260
286
  visitor(node);
261
287
  };
262
288
  continue;
263
289
  }
264
290
  if (key === "JSXOpeningElement") {
265
291
  wrappedVisitors.JSXOpeningElement = (node) => {
266
- if (!fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
292
+ if (!fileImportsReactRuntime && !fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
267
293
  if (fileIsNonReactJsx) return;
268
294
  visitor(node);
269
295
  };
@@ -275,7 +301,9 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
275
301
  };
276
302
  }
277
303
  if (!("Program" in wrappedVisitors)) wrappedVisitors.Program = (node) => {
278
- fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
304
+ const runtimeImports = collectJsxRuntimeImports(node);
305
+ fileImportsReactRuntime = runtimeImports.hasReactRuntime;
306
+ fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
279
307
  };
280
308
  return wrappedVisitors;
281
309
  });
@@ -2312,7 +2340,7 @@ const anchorAmbiguousText = defineRule({
2312
2340
  });
2313
2341
  //#endregion
2314
2342
  //#region src/plugin/rules/a11y/anchor-has-content.ts
2315
- const MESSAGE$61 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2343
+ const MESSAGE$62 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2316
2344
  const isTransComponentsTemplate = (node) => {
2317
2345
  let current = node.parent;
2318
2346
  while (current) {
@@ -2348,7 +2376,7 @@ const anchorHasContent = defineRule({
2348
2376
  if (isTransComponentsTemplate(node)) return;
2349
2377
  context.report({
2350
2378
  node: opening.name,
2351
- message: MESSAGE$61
2379
+ message: MESSAGE$62
2352
2380
  });
2353
2381
  } };
2354
2382
  }
@@ -2780,7 +2808,7 @@ const parseJsxValue = (value) => {
2780
2808
  };
2781
2809
  //#endregion
2782
2810
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
2783
- const MESSAGE$60 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2811
+ const MESSAGE$61 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2784
2812
  const mayBeContentEditable = (node) => {
2785
2813
  const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
2786
2814
  if (!attribute) return false;
@@ -2811,7 +2839,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2811
2839
  if (tabIndexValue === null || tabIndexValue >= -1) return;
2812
2840
  context.report({
2813
2841
  node: node.name,
2814
- message: MESSAGE$60
2842
+ message: MESSAGE$61
2815
2843
  });
2816
2844
  return;
2817
2845
  }
@@ -2819,7 +2847,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2819
2847
  if (mayBeContentEditable(node)) return;
2820
2848
  context.report({
2821
2849
  node: node.name,
2822
- message: MESSAGE$60
2850
+ message: MESSAGE$61
2823
2851
  });
2824
2852
  } })
2825
2853
  });
@@ -4314,6 +4342,397 @@ const findTransparentExpressionRoot = (node) => {
4314
4342
  return current;
4315
4343
  };
4316
4344
  //#endregion
4345
+ //#region src/plugin/utils/get-static-property-key-name.ts
4346
+ const getStaticPropertyKeyName = (node, options = {}) => {
4347
+ if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
4348
+ const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
4349
+ if (node.computed) {
4350
+ if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
4351
+ if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
4352
+ return null;
4353
+ }
4354
+ if (isNodeOfType(key, "Identifier")) return key.name;
4355
+ if (isNodeOfType(key, "Literal")) {
4356
+ if (typeof key.value === "string") return key.value;
4357
+ if (options.stringifyNonStringLiterals) return String(key.value);
4358
+ }
4359
+ return null;
4360
+ };
4361
+ //#endregion
4362
+ //#region src/plugin/utils/get-destructured-binding-property-name.ts
4363
+ const getDestructuredBindingPropertyName = (bindingIdentifier) => {
4364
+ let bindingNode = bindingIdentifier;
4365
+ if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
4366
+ const property = bindingNode.parent;
4367
+ if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
4368
+ return getStaticPropertyKeyName(property, { allowComputedString: true });
4369
+ };
4370
+ //#endregion
4371
+ //#region src/plugin/utils/get-static-property-name.ts
4372
+ const getStaticPropertyName = (memberExpression) => {
4373
+ const property = memberExpression.property;
4374
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
4375
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
4376
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
4377
+ return null;
4378
+ };
4379
+ //#endregion
4380
+ //#region src/plugin/utils/has-static-property-write-before.ts
4381
+ const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
4382
+ const potentiallyAliasedSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
4383
+ const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
4384
+ "CatchClause",
4385
+ "ConditionalExpression",
4386
+ "DoWhileStatement",
4387
+ "ForInStatement",
4388
+ "ForOfStatement",
4389
+ "ForStatement",
4390
+ "IfStatement",
4391
+ "LogicalExpression",
4392
+ "SwitchCase",
4393
+ "SwitchStatement",
4394
+ "TryStatement",
4395
+ "WhileStatement"
4396
+ ]);
4397
+ const getResolvedStaticPropertyName = (memberExpression, scopes) => {
4398
+ if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
4399
+ const directPropertyName = getStaticPropertyName(memberExpression);
4400
+ if (directPropertyName || !memberExpression.computed) return directPropertyName;
4401
+ const property = stripParenExpression(memberExpression.property);
4402
+ if (!isNodeOfType(property, "Identifier")) return null;
4403
+ const propertySymbol = resolveConstIdentifierAlias(property, scopes);
4404
+ const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
4405
+ return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
4406
+ };
4407
+ const collectScopeSymbols = (scope, symbols) => {
4408
+ symbols.push(...scope.symbols);
4409
+ for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
4410
+ };
4411
+ const getEquivalentSymbols = (identifier, scopes) => {
4412
+ const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
4413
+ if (!rootSymbol) return [];
4414
+ let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
4415
+ if (!symbolsByRootId) {
4416
+ symbolsByRootId = /* @__PURE__ */ new Map();
4417
+ equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
4418
+ }
4419
+ const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
4420
+ if (cachedSymbols) return cachedSymbols;
4421
+ const allSymbols = [];
4422
+ collectScopeSymbols(scopes.rootScope, allSymbols);
4423
+ const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
4424
+ symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
4425
+ return equivalentSymbols;
4426
+ };
4427
+ const getDirectAliasSourceSymbol = (expression, scopes) => {
4428
+ const unwrappedExpression = stripParenExpression(expression);
4429
+ return isNodeOfType(unwrappedExpression, "Identifier") ? scopes.symbolFor(unwrappedExpression) : null;
4430
+ };
4431
+ const getDirectAssignmentSourceSymbol = (identifier, scopes) => {
4432
+ const assignmentTarget = findTransparentExpressionRoot(identifier);
4433
+ const parent = assignmentTarget.parent;
4434
+ if (!parent || !isNodeOfType(parent, "AssignmentExpression") || parent.operator !== "=" || parent.left !== assignmentTarget) return null;
4435
+ return getDirectAliasSourceSymbol(parent.right, scopes);
4436
+ };
4437
+ const isDirectAliasSourceReference = (identifier) => {
4438
+ const aliasSource = findTransparentExpressionRoot(identifier);
4439
+ const parent = aliasSource.parent;
4440
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === aliasSource && (isNodeOfType(parent.id, "Identifier") || isNodeOfType(parent.id, "ObjectPattern"))) return true;
4441
+ return Boolean(parent && isNodeOfType(parent, "AssignmentExpression") && parent.operator === "=" && parent.right === aliasSource && isNodeOfType(stripParenExpression(parent.left), "Identifier"));
4442
+ };
4443
+ const isDirectAliasOfKnownSymbol = (symbol, knownSymbolIds, scopes) => {
4444
+ if (symbol.initializer && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && symbol.declarationNode.id === symbol.bindingIdentifier) {
4445
+ const initializerSymbol = getDirectAliasSourceSymbol(symbol.initializer, scopes);
4446
+ if (initializerSymbol && knownSymbolIds.has(initializerSymbol.id)) return true;
4447
+ }
4448
+ return symbol.references.some((reference) => {
4449
+ const assignmentSourceSymbol = getDirectAssignmentSourceSymbol(reference.identifier, scopes);
4450
+ return Boolean(assignmentSourceSymbol && knownSymbolIds.has(assignmentSourceSymbol.id));
4451
+ });
4452
+ };
4453
+ const getPotentiallyAliasedSymbols = (identifier, scopes) => {
4454
+ const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
4455
+ if (!rootSymbol) return [];
4456
+ let symbolsByRootId = potentiallyAliasedSymbolsByAnalysis.get(scopes);
4457
+ if (!symbolsByRootId) {
4458
+ symbolsByRootId = /* @__PURE__ */ new Map();
4459
+ potentiallyAliasedSymbolsByAnalysis.set(scopes, symbolsByRootId);
4460
+ }
4461
+ const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
4462
+ if (cachedSymbols) return cachedSymbols;
4463
+ const allSymbols = [];
4464
+ collectScopeSymbols(scopes.rootScope, allSymbols);
4465
+ const aliasedSymbolIds = new Set([rootSymbol.id]);
4466
+ let didAddAlias = true;
4467
+ while (didAddAlias) {
4468
+ didAddAlias = false;
4469
+ for (const symbol of allSymbols) {
4470
+ if (aliasedSymbolIds.has(symbol.id)) continue;
4471
+ if (!isDirectAliasOfKnownSymbol(symbol, aliasedSymbolIds, scopes)) continue;
4472
+ aliasedSymbolIds.add(symbol.id);
4473
+ didAddAlias = true;
4474
+ }
4475
+ }
4476
+ const aliasedSymbols = allSymbols.filter((symbol) => aliasedSymbolIds.has(symbol.id));
4477
+ symbolsByRootId.set(rootSymbol.id, aliasedSymbols);
4478
+ return aliasedSymbols;
4479
+ };
4480
+ const findExecutionBoundary = (node) => {
4481
+ let current = node;
4482
+ while (current) {
4483
+ if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
4484
+ current = current.parent ?? null;
4485
+ }
4486
+ return null;
4487
+ };
4488
+ const isOnUnconditionalPath = (node, boundary) => {
4489
+ let current = node.parent ?? null;
4490
+ while (current && current !== boundary) {
4491
+ if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
4492
+ current = current.parent ?? null;
4493
+ }
4494
+ return current === boundary;
4495
+ };
4496
+ const findFunctionBindingIdentifier = (functionNode) => {
4497
+ if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
4498
+ const parent = functionNode.parent;
4499
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
4500
+ return null;
4501
+ };
4502
+ const findDirectCall = (identifier) => {
4503
+ let callee = identifier;
4504
+ let parent = callee.parent;
4505
+ while (parent && stripParenExpression(parent) === identifier) {
4506
+ callee = parent;
4507
+ parent = callee.parent;
4508
+ }
4509
+ return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
4510
+ };
4511
+ const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
4512
+ if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
4513
+ visitedFunctionNodes.add(functionNode);
4514
+ const referenceBoundary = findExecutionBoundary(referenceNode);
4515
+ if (!referenceBoundary) return false;
4516
+ const invocationCalls = [];
4517
+ const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
4518
+ if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
4519
+ const call = findDirectCall(reference.identifier);
4520
+ if (call) invocationCalls.push(call);
4521
+ }
4522
+ else {
4523
+ const call = findDirectCall(functionNode);
4524
+ if (call) invocationCalls.push(call);
4525
+ }
4526
+ return invocationCalls.some((call) => {
4527
+ if (call.range[0] >= referenceNode.range[0]) return false;
4528
+ const callBoundary = findExecutionBoundary(call);
4529
+ if (!callBoundary) return false;
4530
+ if (callBoundary === referenceBoundary) return true;
4531
+ if (!isFunctionLike$2(callBoundary)) return false;
4532
+ return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
4533
+ });
4534
+ };
4535
+ const isMemberWriteTarget = (memberExpression) => {
4536
+ const parent = memberExpression.parent;
4537
+ if (!parent) return false;
4538
+ if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
4539
+ if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
4540
+ return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
4541
+ };
4542
+ const getMemberWriteTarget = (identifier) => {
4543
+ let parent = identifier.parent;
4544
+ while (parent && stripParenExpression(parent) === identifier) parent = parent.parent;
4545
+ if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== identifier || !isMemberWriteTarget(parent)) return null;
4546
+ return parent;
4547
+ };
4548
+ const getStaticPropertyWriteTarget = (identifier, propertyName, scopes) => {
4549
+ const writeTarget = getMemberWriteTarget(identifier);
4550
+ return writeTarget && getResolvedStaticPropertyName(writeTarget, scopes) === propertyName ? writeTarget : null;
4551
+ };
4552
+ const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
4553
+ const writeTarget = getStaticPropertyWriteTarget(reference.identifier, propertyName, scopes);
4554
+ if (!writeTarget) return false;
4555
+ const writeBoundary = findExecutionBoundary(writeTarget);
4556
+ const referenceBoundary = findExecutionBoundary(referenceNode);
4557
+ if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(writeTarget, writeBoundary)) return false;
4558
+ if (writeBoundary === referenceBoundary) return writeTarget.range[0] < referenceNode.range[0];
4559
+ return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
4560
+ });
4561
+ const isStableStaticPropertyReference = (identifier) => {
4562
+ if (isDirectAliasSourceReference(identifier)) return true;
4563
+ const identifierRoot = findTransparentExpressionRoot(identifier);
4564
+ const memberExpression = identifierRoot.parent;
4565
+ return Boolean(memberExpression && isNodeOfType(memberExpression, "MemberExpression") && stripParenExpression(memberExpression.object) === identifierRoot);
4566
+ };
4567
+ const hasPossibleStaticPropertyWrite = (identifier, propertyName, scopes) => {
4568
+ if (!isNodeOfType(identifier, "Identifier")) return false;
4569
+ return getPotentiallyAliasedSymbols(identifier, scopes).some((symbol) => symbol.references.some((reference) => {
4570
+ const writeTarget = getMemberWriteTarget(reference.identifier);
4571
+ if (!writeTarget) return false;
4572
+ const writtenPropertyName = getResolvedStaticPropertyName(writeTarget, scopes);
4573
+ return writtenPropertyName === null || writtenPropertyName === propertyName;
4574
+ }));
4575
+ };
4576
+ const hasPossibleStaticPropertyMutationOrEscape = (identifier, propertyName, scopes) => {
4577
+ if (!isNodeOfType(identifier, "Identifier")) return false;
4578
+ if (hasPossibleStaticPropertyWrite(identifier, propertyName, scopes)) return true;
4579
+ return getPotentiallyAliasedSymbols(identifier, scopes).some((symbol) => symbol.references.some((reference) => !isStableStaticPropertyReference(reference.identifier)));
4580
+ };
4581
+ const hasPossibleStaticMemberCallWrite = (callExpression, scopes) => {
4582
+ const unwrappedCallExpression = stripParenExpression(callExpression);
4583
+ if (!isNodeOfType(unwrappedCallExpression, "CallExpression")) return false;
4584
+ const callee = stripParenExpression(unwrappedCallExpression.callee);
4585
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
4586
+ const propertyName = getStaticPropertyName(callee);
4587
+ if (propertyName === null) return false;
4588
+ const receiver = stripParenExpression(callee.object);
4589
+ return isNodeOfType(receiver, "Identifier") && hasPossibleStaticPropertyWrite(receiver, propertyName, scopes);
4590
+ };
4591
+ const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
4592
+ if (!isNodeOfType(identifier, "Identifier")) return false;
4593
+ return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
4594
+ };
4595
+ //#endregion
4596
+ //#region src/plugin/utils/find-enclosing-function.ts
4597
+ const findEnclosingFunction$1 = (node) => {
4598
+ let cursor = node.parent;
4599
+ while (cursor) {
4600
+ if (isFunctionLike$2(cursor)) return cursor;
4601
+ cursor = cursor.parent ?? null;
4602
+ }
4603
+ return null;
4604
+ };
4605
+ //#endregion
4606
+ //#region src/plugin/utils/has-symbol-write-before.ts
4607
+ const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
4608
+ if (reference.flag === "read") return false;
4609
+ const writeFunction = findEnclosingFunction$1(reference.identifier);
4610
+ if (writeFunction === findEnclosingFunction$1(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
4611
+ if (!writeFunction) return true;
4612
+ return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
4613
+ });
4614
+ //#endregion
4615
+ //#region src/plugin/utils/get-order-independent-local-function.ts
4616
+ const COMMUTATIVE_COMPOUND_ASSIGNMENT_OPERATORS = new Set([
4617
+ "+=",
4618
+ "-=",
4619
+ "*=",
4620
+ "/=",
4621
+ "%=",
4622
+ "&=",
4623
+ "^=",
4624
+ "|="
4625
+ ]);
4626
+ const isPureParameterExpression = (expression, parameterNames) => {
4627
+ const unwrappedExpression = stripParenExpression(expression);
4628
+ if (isNodeOfType(unwrappedExpression, "Literal")) return true;
4629
+ if (isNodeOfType(unwrappedExpression, "Identifier")) return parameterNames.has(unwrappedExpression.name);
4630
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression") || isNodeOfType(unwrappedExpression, "LogicalExpression")) return isPureParameterExpression(unwrappedExpression.left, parameterNames) && isPureParameterExpression(unwrappedExpression.right, parameterNames);
4631
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression")) return unwrappedExpression.operator !== "delete" && isPureParameterExpression(unwrappedExpression.argument, parameterNames);
4632
+ if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isPureParameterExpression(unwrappedExpression.test, parameterNames) && isPureParameterExpression(unwrappedExpression.consequent, parameterNames) && isPureParameterExpression(unwrappedExpression.alternate, parameterNames);
4633
+ if (isNodeOfType(unwrappedExpression, "TemplateLiteral")) return unwrappedExpression.expressions.every((nestedExpression) => isPureParameterExpression(nestedExpression, parameterNames));
4634
+ return false;
4635
+ };
4636
+ const isOrderIndependentPromiseResolveCall = (expression, parameterNames, scopes) => {
4637
+ const unwrappedExpression = stripParenExpression(expression);
4638
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
4639
+ if (!unwrappedExpression.arguments.every((argument) => !isNodeOfType(argument, "SpreadElement") && isPureParameterExpression(argument, parameterNames))) return false;
4640
+ const callee = stripParenExpression(unwrappedExpression.callee);
4641
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
4642
+ if (getStaticPropertyName(callee) !== "resolve") return false;
4643
+ const receiver = stripParenExpression(callee.object);
4644
+ return isNodeOfType(receiver, "Identifier") && receiver.name === "Promise" && scopes.isGlobalReference(receiver);
4645
+ };
4646
+ const isHarmlessPromiseResolveAwait = (statement, parameterNames, scopes) => {
4647
+ if (!isNodeOfType(statement, "ExpressionStatement") || !isNodeOfType(statement.expression, "AwaitExpression")) return false;
4648
+ return isOrderIndependentPromiseResolveCall(statement.expression.argument, parameterNames, scopes);
4649
+ };
4650
+ const isCommutativeParameterMutation = (statement, parameterNames) => {
4651
+ if (!isNodeOfType(statement, "ExpressionStatement") || !isNodeOfType(statement.expression, "AssignmentExpression") || !COMMUTATIVE_COMPOUND_ASSIGNMENT_OPERATORS.has(statement.expression.operator)) return false;
4652
+ const mutationTarget = stripParenExpression(statement.expression.left);
4653
+ if (!isNodeOfType(mutationTarget, "MemberExpression")) return false;
4654
+ if (getStaticPropertyName(mutationTarget) === null) return false;
4655
+ const receiver = stripParenExpression(mutationTarget.object);
4656
+ return isNodeOfType(receiver, "Identifier") && parameterNames.has(receiver.name) && isNodeOfType(stripParenExpression(statement.expression.right), "Literal");
4657
+ };
4658
+ const isOrderIndependentFunction = (functionNode, scopes) => {
4659
+ if (!isFunctionLike$2(functionNode)) return false;
4660
+ const parameterNames = /* @__PURE__ */ new Set();
4661
+ for (const parameter of functionNode.params) {
4662
+ if (!isNodeOfType(parameter, "Identifier")) return false;
4663
+ parameterNames.add(parameter.name);
4664
+ }
4665
+ if (!functionNode.async) {
4666
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isOrderIndependentPromiseResolveCall(functionNode.body, parameterNames, scopes);
4667
+ if (functionNode.body.body.length !== 1) return false;
4668
+ const [returnStatement] = functionNode.body.body;
4669
+ return Boolean(isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument && isOrderIndependentPromiseResolveCall(returnStatement.argument, parameterNames, scopes));
4670
+ }
4671
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isPureParameterExpression(functionNode.body, parameterNames);
4672
+ const statements = functionNode.body.body;
4673
+ for (let statementIndex = 0; statementIndex < statements.length; statementIndex++) {
4674
+ const statement = statements[statementIndex];
4675
+ const isTerminalStatement = statementIndex === statements.length - 1;
4676
+ if (isHarmlessPromiseResolveAwait(statement, parameterNames, scopes)) continue;
4677
+ if (isNodeOfType(statement, "ExpressionStatement") && isPureParameterExpression(statement.expression, parameterNames)) continue;
4678
+ if (isCommutativeParameterMutation(statement, parameterNames)) return isTerminalStatement;
4679
+ if (!isNodeOfType(statement, "ReturnStatement") || !isTerminalStatement) return false;
4680
+ return !statement.argument || isPureParameterExpression(statement.argument, parameterNames) || isOrderIndependentPromiseResolveCall(statement.argument, parameterNames, scopes);
4681
+ }
4682
+ return true;
4683
+ };
4684
+ const getObjectPropertyName = (property) => {
4685
+ if (!isNodeOfType(property, "Property")) return null;
4686
+ return getStaticPropertyKeyName(property, { allowComputedString: true });
4687
+ };
4688
+ const resolveOrderIndependentObjectPropertyFunction = (objectExpression, propertyName, callExpression, scopes, visitedSymbolIds) => {
4689
+ const unwrappedObject = stripParenExpression(objectExpression);
4690
+ if (isNodeOfType(unwrappedObject, "Identifier")) {
4691
+ const symbol = scopes.symbolFor(unwrappedObject);
4692
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, callExpression, scopes) || hasPossibleStaticPropertyMutationOrEscape(unwrappedObject, propertyName, scopes)) return null;
4693
+ visitedSymbolIds.add(symbol.id);
4694
+ return resolveOrderIndependentObjectPropertyFunction(symbol.initializer, propertyName, callExpression, scopes, visitedSymbolIds);
4695
+ }
4696
+ if (!isNodeOfType(unwrappedObject, "ObjectExpression")) return null;
4697
+ let matchingProperty = null;
4698
+ for (const property of unwrappedObject.properties) {
4699
+ if (!isNodeOfType(property, "Property")) return null;
4700
+ if (property.kind !== "init") return null;
4701
+ const candidatePropertyName = getObjectPropertyName(property);
4702
+ if (candidatePropertyName === null) return null;
4703
+ if (candidatePropertyName === propertyName) matchingProperty = property;
4704
+ }
4705
+ if (!matchingProperty || !isNodeOfType(matchingProperty, "Property")) return null;
4706
+ const propertyValue = stripParenExpression(matchingProperty.value);
4707
+ if (isFunctionLike$2(propertyValue)) return isOrderIndependentFunction(propertyValue, scopes) ? propertyValue : null;
4708
+ return resolveOrderIndependentLocalFunction(propertyValue, callExpression, scopes, visitedSymbolIds);
4709
+ };
4710
+ const resolveOrderIndependentLocalFunction = (callee, callExpression, scopes, visitedSymbolIds) => {
4711
+ const unwrappedCallee = stripParenExpression(callee);
4712
+ if (isNodeOfType(unwrappedCallee, "MemberExpression")) {
4713
+ const propertyName = getStaticPropertyName(unwrappedCallee);
4714
+ if (propertyName === null) return null;
4715
+ return resolveOrderIndependentObjectPropertyFunction(stripParenExpression(unwrappedCallee.object), propertyName, callExpression, scopes, visitedSymbolIds);
4716
+ }
4717
+ if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
4718
+ const symbol = scopes.symbolFor(unwrappedCallee);
4719
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, callExpression, scopes)) return null;
4720
+ visitedSymbolIds.add(symbol.id);
4721
+ if (!symbol.initializer) return null;
4722
+ const initializer = stripParenExpression(symbol.initializer);
4723
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
4724
+ if (destructuredPropertyName !== null) return resolveOrderIndependentObjectPropertyFunction(initializer, destructuredPropertyName, callExpression, scopes, visitedSymbolIds);
4725
+ if (isFunctionLike$2(initializer)) return isOrderIndependentFunction(initializer, scopes) ? initializer : null;
4726
+ if (symbol.kind !== "const") return null;
4727
+ return resolveOrderIndependentLocalFunction(initializer, callExpression, scopes, visitedSymbolIds);
4728
+ };
4729
+ const getOrderIndependentLocalFunction = (callExpression, scopes) => {
4730
+ const unwrappedCall = stripParenExpression(callExpression);
4731
+ if (!isNodeOfType(unwrappedCall, "CallExpression")) return null;
4732
+ if (hasPossibleStaticMemberCallWrite(unwrappedCall, scopes)) return null;
4733
+ return resolveOrderIndependentLocalFunction(unwrappedCall.callee, unwrappedCall, scopes, /* @__PURE__ */ new Set());
4734
+ };
4735
+ //#endregion
4317
4736
  //#region src/plugin/utils/is-inline-function-expression.ts
4318
4737
  /**
4319
4738
  * Type-guard for the two "inline function expression" ESTree forms:
@@ -4347,13 +4766,15 @@ const isIntentionalSequencingCallee = (callee) => {
4347
4766
  if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier")) return INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(callee.property.name);
4348
4767
  return false;
4349
4768
  };
4350
- const isAwaitingSleepLikeCall = (awaitNode) => {
4769
+ const isAwaitingSleepLikeCall = (awaitNode, context) => {
4351
4770
  if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
4352
4771
  const argument = awaitNode.argument;
4353
4772
  if (!argument) return false;
4354
4773
  if (!isNodeOfType(argument, "CallExpression")) return false;
4774
+ if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) return false;
4355
4775
  return isIntentionalSequencingCallee(argument.callee);
4356
4776
  };
4777
+ const isAwaitingPossiblyMutatedMemberCall = (awaitNode, context) => isNodeOfType(awaitNode, "AwaitExpression") && Boolean(awaitNode.argument) && hasPossibleStaticMemberCallWrite(awaitNode.argument, context.scopes);
4357
4778
  const PROMISE_CONCURRENCY_METHODS = new Set([
4358
4779
  "all",
4359
4780
  "allSettled",
@@ -4396,7 +4817,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
4396
4817
  });
4397
4818
  return isWaitLike;
4398
4819
  };
4399
- const isIntentionallySequentialAwait = (awaitNode) => isAwaitingSleepLikeCall(awaitNode) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
4820
+ const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
4400
4821
  const collectPatternIdentifiers = (pattern, target) => {
4401
4822
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
4402
4823
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -4587,12 +5008,12 @@ const getLoopLabelName = (loopNode) => {
4587
5008
  if (isNodeOfType(parent, "LabeledStatement") && isNodeOfType(parent.label, "Identifier")) return parent.label.name;
4588
5009
  return null;
4589
5010
  };
4590
- const loopBodyHasIntentionallySequentialAwait = (block) => {
5011
+ const loopBodyHasIntentionallySequentialAwait = (block, context) => {
4591
5012
  let foundIntentional = false;
4592
5013
  walkAst(block, (child) => {
4593
5014
  if (foundIntentional) return false;
4594
5015
  if (isInlineFunctionExpression(child) || isNodeOfType(child, "FunctionDeclaration")) return false;
4595
- if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child)) {
5016
+ if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child, context)) {
4596
5017
  foundIntentional = true;
4597
5018
  return false;
4598
5019
  }
@@ -4697,7 +5118,7 @@ const asyncAwaitInLoop = defineRule({
4697
5118
  const inspectLoop = (loopNode, label) => {
4698
5119
  const loopBody = loopNode.body;
4699
5120
  if (!loopBody) return;
4700
- if (loopBodyHasIntentionallySequentialAwait(loopBody)) return;
5121
+ if (loopBodyHasIntentionallySequentialAwait(loopBody, context)) return;
4701
5122
  if ((isNodeOfType(loopNode, "WhileStatement") || isNodeOfType(loopNode, "DoWhileStatement")) && isLoopTestDependentOnBodyState(loopNode.test, loopBody)) return;
4702
5123
  if (hasLoopCarriedDependency(loopBody)) return;
4703
5124
  if (loopBodyHasAwaitDependentEarlyExit(loopBody, getLoopLabelName(loopNode))) return;
@@ -4957,7 +5378,7 @@ const guardConsequentPerformsSideEffects = (consequent) => {
4957
5378
  });
4958
5379
  return performsSideEffects;
4959
5380
  };
4960
- const findEnclosingFunction$1 = (node) => {
5381
+ const findEnclosingFunction = (node) => {
4961
5382
  let ancestor = node.parent;
4962
5383
  while (ancestor) {
4963
5384
  if (isFunctionLike$2(ancestor)) return ancestor;
@@ -4970,7 +5391,7 @@ const guardTestReadsReassignedLocal = (test, guardStatement) => {
4970
5391
  const testIdentifierNames = /* @__PURE__ */ new Set();
4971
5392
  collectReferenceIdentifierNames(test, testIdentifierNames);
4972
5393
  if (testIdentifierNames.size === 0) return false;
4973
- const enclosingFunction = findEnclosingFunction$1(guardStatement);
5394
+ const enclosingFunction = findEnclosingFunction(guardStatement);
4974
5395
  if (!enclosingFunction || !isFunctionLike$2(enclosingFunction) || !enclosingFunction.body) return false;
4975
5396
  let readsReassignedLocal = false;
4976
5397
  walkAst(enclosingFunction.body, (child) => {
@@ -5141,19 +5562,36 @@ const isIntentionalSequencingAwait = (awaitedCall) => {
5141
5562
  return getCalleeIdentifierTrail(awaitedCall).some((name) => INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(name));
5142
5563
  };
5143
5564
  const isBareExpressionAwait = (statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "AwaitExpression");
5565
+ const hasOrderIndependentBareAwaitArguments = (callExpression) => {
5566
+ const unwrappedCallExpression = stripParenExpression(callExpression);
5567
+ if (!isNodeOfType(unwrappedCallExpression, "CallExpression")) return false;
5568
+ return unwrappedCallExpression.arguments.every((argument) => {
5569
+ if (isNodeOfType(argument, "SpreadElement")) return false;
5570
+ const unwrappedArgument = stripParenExpression(argument);
5571
+ return isNodeOfType(unwrappedArgument, "Identifier") || isNodeOfType(unwrappedArgument, "Literal");
5572
+ });
5573
+ };
5144
5574
  const isNonCallAwait = (statement) => {
5145
5575
  const awaitedExpression = getAwaitedCall(statement);
5146
5576
  if (!awaitedExpression) return false;
5147
5577
  const stripped = stripParenExpression(awaitedExpression);
5148
5578
  return !isNodeOfType(stripped, "CallExpression") && !isNodeOfType(stripped, "NewExpression") && !isNodeOfType(stripped, "ImportExpression");
5149
5579
  };
5150
- const sequenceContainsSerializationSignal = (statements) => {
5580
+ const sequenceContainsSerializationSignal = (statements, context) => {
5581
+ let bareAwaitFunction = null;
5151
5582
  for (const statement of statements) {
5152
- if (isBareExpressionAwait(statement)) return true;
5153
5583
  if (isNonCallAwait(statement)) return true;
5154
5584
  const awaitedCall = getAwaitedCall(statement);
5585
+ if (awaitedCall && hasPossibleStaticMemberCallWrite(awaitedCall, context.scopes)) return true;
5586
+ const orderIndependentFunction = awaitedCall ? getOrderIndependentLocalFunction(awaitedCall, context.scopes) : null;
5587
+ if (isBareExpressionAwait(statement)) {
5588
+ if (orderIndependentFunction === null) return true;
5589
+ if (!awaitedCall || !hasOrderIndependentBareAwaitArguments(awaitedCall)) return true;
5590
+ if (bareAwaitFunction !== null && bareAwaitFunction !== orderIndependentFunction) return true;
5591
+ bareAwaitFunction = orderIndependentFunction;
5592
+ }
5155
5593
  if (isOrderedUiFlowAwait(awaitedCall)) return true;
5156
- if (isIntentionalSequencingAwait(awaitedCall)) return true;
5594
+ if (isIntentionalSequencingAwait(awaitedCall) && orderIndependentFunction === null) return true;
5157
5595
  }
5158
5596
  return false;
5159
5597
  };
@@ -5211,7 +5649,7 @@ const asyncParallel = defineRule({
5211
5649
  const consecutiveAwaitStatements = [];
5212
5650
  const flushConsecutiveAwaits = () => {
5213
5651
  if (consecutiveAwaitStatements.length >= 3) {
5214
- if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements)) reportIfIndependent(consecutiveAwaitStatements, context);
5652
+ if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements, context)) reportIfIndependent(consecutiveAwaitStatements, context);
5215
5653
  }
5216
5654
  consecutiveAwaitStatements.length = 0;
5217
5655
  };
@@ -5224,7 +5662,7 @@ const asyncParallel = defineRule({
5224
5662
  });
5225
5663
  //#endregion
5226
5664
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
5227
- const MESSAGE$59 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
5665
+ const MESSAGE$60 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
5228
5666
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
5229
5667
  const STORAGE_GLOBALS = new Set([
5230
5668
  "window",
@@ -5289,7 +5727,7 @@ const authTokenInWebStorage = defineRule({
5289
5727
  if (keyString === null || !isAuthCredentialKey(keyString)) return;
5290
5728
  context.report({
5291
5729
  node,
5292
- message: MESSAGE$59
5730
+ message: MESSAGE$60
5293
5731
  });
5294
5732
  },
5295
5733
  AssignmentExpression(node) {
@@ -5300,7 +5738,7 @@ const authTokenInWebStorage = defineRule({
5300
5738
  if (!propertyName || !isAuthCredentialKey(propertyName)) return;
5301
5739
  context.report({
5302
5740
  node: target,
5303
- message: MESSAGE$59
5741
+ message: MESSAGE$60
5304
5742
  });
5305
5743
  }
5306
5744
  }))
@@ -5439,7 +5877,7 @@ const CI_INSTALL_NEAR_SECRET_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b
5439
5877
  const INSTALL_COMMAND_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b/i;
5440
5878
  const SECRET_REFERENCE_PATTERN = /\bsecrets\.[A-Z0-9_]+/;
5441
5879
  const IGNORE_SCRIPTS_FLAG_PATTERN = /--ignore-scripts\b/;
5442
- const MESSAGE$58 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5880
+ const MESSAGE$59 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5443
5881
  const isWorkflowPath = (relativePath) => /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath);
5444
5882
  const scanWorkflowContent = (content) => {
5445
5883
  const lines = content.split("\n");
@@ -5484,7 +5922,7 @@ const scanWorkflowContent = (content) => {
5484
5922
  const installLineOffset = Math.max(step.lines.findIndex((stepLine) => INSTALL_COMMAND_PATTERN.test(stepLine)), 0);
5485
5923
  const installColumnIndex = step.lines[installLineOffset].search(INSTALL_COMMAND_PATTERN);
5486
5924
  return [{
5487
- message: MESSAGE$58,
5925
+ message: MESSAGE$59,
5488
5926
  line: step.startLineIndex + installLineOffset + 1,
5489
5927
  column: (installColumnIndex === -1 ? 0 : installColumnIndex) + 1
5490
5928
  }];
@@ -5494,7 +5932,7 @@ const scanWorkflowContent = (content) => {
5494
5932
  const scanNonWorkflowConfig = scanByPattern({
5495
5933
  shouldScan: (file) => isConfigOrCiPath(file.relativePath) && !file.relativePath.endsWith("package.json") && !isWorkflowPath(file.relativePath),
5496
5934
  pattern: CI_INSTALL_NEAR_SECRET_PATTERN,
5497
- message: MESSAGE$58
5935
+ message: MESSAGE$59
5498
5936
  });
5499
5937
  const scan = (file) => {
5500
5938
  if (isWorkflowPath(file.relativePath)) return scanWorkflowContent(file.content);
@@ -5989,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;
@@ -6263,7 +6684,7 @@ const isPureEventBlockerHandler = (attribute) => {
6263
6684
  };
6264
6685
  //#endregion
6265
6686
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
6266
- const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6687
+ const MESSAGE$58 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6267
6688
  const KEY_HANDLERS = [
6268
6689
  "onKeyUp",
6269
6690
  "onKeyDown",
@@ -6474,7 +6895,7 @@ const clickEventsHaveKeyEvents = defineRule({
6474
6895
  if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6475
6896
  context.report({
6476
6897
  node: node.name,
6477
- message: MESSAGE$57
6898
+ message: MESSAGE$58
6478
6899
  });
6479
6900
  } };
6480
6901
  }
@@ -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);
@@ -8727,7 +9129,7 @@ const getClassNameLiteral = (classAttribute) => {
8727
9129
  };
8728
9130
  //#endregion
8729
9131
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
8730
- const MESSAGE$56 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
9132
+ const MESSAGE$57 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
8731
9133
  const NON_OPERABLE_ELEMENTS = new Set([
8732
9134
  "td",
8733
9135
  "th",
@@ -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;
@@ -9165,7 +9659,7 @@ const controlHasAssociatedLabel = defineRule({
9165
9659
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
9166
9660
  context.report({
9167
9661
  node: candidate.opening,
9168
- message: MESSAGE$56
9662
+ message: MESSAGE$57
9169
9663
  });
9170
9664
  }
9171
9665
  }
@@ -9926,7 +10420,7 @@ const noVagueButtonLabel = defineRule({
9926
10420
  });
9927
10421
  //#endregion
9928
10422
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
9929
- const MESSAGE$55 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
10423
+ const MESSAGE$56 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
9930
10424
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
9931
10425
  const NAME_PROVIDING_ATTRIBUTES = [
9932
10426
  "aria-label",
@@ -9951,7 +10445,7 @@ const dialogHasAccessibleName = defineRule({
9951
10445
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
9952
10446
  context.report({
9953
10447
  node: node.name,
9954
- message: MESSAGE$55
10448
+ message: MESSAGE$56
9955
10449
  });
9956
10450
  } };
9957
10451
  }
@@ -9991,7 +10485,7 @@ const isEs6Component = (node) => {
9991
10485
  };
9992
10486
  //#endregion
9993
10487
  //#region src/plugin/rules/react-builtins/display-name.ts
9994
- const MESSAGE$54 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
10488
+ const MESSAGE$55 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
9995
10489
  const DEFAULT_ADDITIONAL_HOCS = [
9996
10490
  "observer",
9997
10491
  "lazy",
@@ -10184,7 +10678,7 @@ const displayName = defineRule({
10184
10678
  const reportAt = (node) => {
10185
10679
  context.report({
10186
10680
  node,
10187
- message: MESSAGE$54
10681
+ message: MESSAGE$55
10188
10682
  });
10189
10683
  };
10190
10684
  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 = [];
@@ -14747,7 +15423,7 @@ const forbidElements = defineRule({
14747
15423
  });
14748
15424
  //#endregion
14749
15425
  //#region src/plugin/rules/react-builtins/forward-ref-uses-ref.ts
14750
- const MESSAGE$53 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
15426
+ const MESSAGE$54 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
14751
15427
  const forwardRefUsesRef = defineRule({
14752
15428
  id: "forward-ref-uses-ref",
14753
15429
  title: "forwardRef without ref parameter",
@@ -14770,7 +15446,7 @@ const forwardRefUsesRef = defineRule({
14770
15446
  if (isNodeOfType(onlyParam, "RestElement")) return;
14771
15447
  context.report({
14772
15448
  node: inner,
14773
- message: MESSAGE$53
15449
+ message: MESSAGE$54
14774
15450
  });
14775
15451
  } })
14776
15452
  });
@@ -14811,7 +15487,7 @@ const gitProviderUrlInjectionRisk = defineRule({
14811
15487
  });
14812
15488
  //#endregion
14813
15489
  //#region src/plugin/rules/a11y/heading-has-content.ts
14814
- const MESSAGE$52 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
15490
+ const MESSAGE$53 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
14815
15491
  const DEFAULT_HEADING_TAGS = [
14816
15492
  "h1",
14817
15493
  "h2",
@@ -14845,7 +15521,7 @@ const headingHasContent = defineRule({
14845
15521
  for (const attribute of ["aria-label", "aria-labelledby"]) if (hasJsxPropIgnoreCase(node.attributes, attribute)) return;
14846
15522
  context.report({
14847
15523
  node,
14848
- message: MESSAGE$52
15524
+ message: MESSAGE$53
14849
15525
  });
14850
15526
  } };
14851
15527
  }
@@ -15009,7 +15685,7 @@ const hooksNoNanInDeps = defineRule({
15009
15685
  });
15010
15686
  //#endregion
15011
15687
  //#region src/plugin/rules/a11y/html-has-lang.ts
15012
- const MESSAGE$51 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
15688
+ const MESSAGE$52 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
15013
15689
  const resolveSettings$39 = (settings) => {
15014
15690
  const reactDoctor = settings?.["react-doctor"];
15015
15691
  return { htmlTags: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.htmlHasLang ?? {} : {}).htmlTags ?? ["html"] };
@@ -15056,13 +15732,13 @@ const htmlHasLang = defineRule({
15056
15732
  if (!lang) {
15057
15733
  context.report({
15058
15734
  node: node.name,
15059
- message: MESSAGE$51
15735
+ message: MESSAGE$52
15060
15736
  });
15061
15737
  return;
15062
15738
  }
15063
15739
  if (evaluateLang(lang.value) === "empty") context.report({
15064
15740
  node: lang,
15065
- message: MESSAGE$51
15741
+ message: MESSAGE$52
15066
15742
  });
15067
15743
  } };
15068
15744
  }
@@ -15302,7 +15978,7 @@ const htmlNoNestedInteractive = defineRule({
15302
15978
  });
15303
15979
  //#endregion
15304
15980
  //#region src/plugin/rules/a11y/iframe-has-title.ts
15305
- const MESSAGE$50 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15981
+ const MESSAGE$51 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15306
15982
  const evaluateTitleValue = (value) => {
15307
15983
  if (!value) return "missing";
15308
15984
  if (isNodeOfType(value, "Literal")) {
@@ -15342,14 +16018,14 @@ const iframeHasTitle = defineRule({
15342
16018
  if (!titleAttr) {
15343
16019
  if (hasSpread || tag === "iframe") context.report({
15344
16020
  node: node.name,
15345
- message: MESSAGE$50
16021
+ message: MESSAGE$51
15346
16022
  });
15347
16023
  return;
15348
16024
  }
15349
16025
  const verdict = evaluateTitleValue(titleAttr.value);
15350
16026
  if (verdict === "missing" || verdict === "empty") context.report({
15351
16027
  node: titleAttr,
15352
- message: MESSAGE$50
16028
+ message: MESSAGE$51
15353
16029
  });
15354
16030
  } })
15355
16031
  });
@@ -15474,7 +16150,7 @@ const iframeMissingSandbox = defineRule({
15474
16150
  });
15475
16151
  //#endregion
15476
16152
  //#region src/plugin/rules/a11y/img-redundant-alt.ts
15477
- const MESSAGE$49 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
16153
+ const MESSAGE$50 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
15478
16154
  const DEFAULT_COMPONENTS = ["img"];
15479
16155
  const DEFAULT_REDUNDANT_WORDS = [
15480
16156
  "image",
@@ -15539,7 +16215,7 @@ const imgRedundantAlt = defineRule({
15539
16215
  if (!altAttribute) return;
15540
16216
  if (altValueRedundant(altAttribute, settings.words)) context.report({
15541
16217
  node: altAttribute,
15542
- message: MESSAGE$49
16218
+ message: MESSAGE$50
15543
16219
  });
15544
16220
  } };
15545
16221
  }
@@ -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";
@@ -17509,7 +18178,7 @@ const getHoistableRegExpConstructionKind = (node, context) => {
17509
18178
  if (!STATEFUL_REGEXP_FLAGS_PATTERN.test(effectiveFlags)) return "stateless";
17510
18179
  return isSafeStatefulReplaceAllSearch(node, effectiveFlags, context) ? "statefulReplaceAll" : null;
17511
18180
  };
17512
- const MESSAGE$48 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
18181
+ const MESSAGE$49 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
17513
18182
  const jsHoistRegexp = defineRule({
17514
18183
  id: "js-hoist-regexp",
17515
18184
  title: "RegExp built inside a loop",
@@ -17526,7 +18195,7 @@ const jsHoistRegexp = defineRule({
17526
18195
  if (constructionKind === "statefulReplaceAll" && cachedEnvironmentHazard === "replaceAllIntegrityLost") return;
17527
18196
  context.report({
17528
18197
  node,
17529
- message: MESSAGE$48
18198
+ message: MESSAGE$49
17530
18199
  });
17531
18200
  };
17532
18201
  return createLoopAwareVisitors({
@@ -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;
@@ -20146,7 +20898,7 @@ const jsxMaxDepth = defineRule({
20146
20898
  });
20147
20899
  //#endregion
20148
20900
  //#region src/plugin/rules/react-builtins/jsx-no-comment-textnodes.ts
20149
- const MESSAGE$47 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20901
+ const MESSAGE$48 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20150
20902
  const LITERAL_TEXT_TAGS = new Set([
20151
20903
  "code",
20152
20904
  "pre",
@@ -20216,7 +20968,7 @@ const jsxNoCommentTextnodes = defineRule({
20216
20968
  if (isDeliberateStyledCommentToken(node)) return;
20217
20969
  context.report({
20218
20970
  node,
20219
- message: MESSAGE$47
20971
+ message: MESSAGE$48
20220
20972
  });
20221
20973
  } })
20222
20974
  });
@@ -20247,7 +20999,7 @@ const isInsideFunctionScope = (node) => {
20247
20999
  };
20248
21000
  //#endregion
20249
21001
  //#region src/plugin/rules/react-builtins/jsx-no-constructed-context-values.ts
20250
- const MESSAGE$46 = "Every reader of this context redraws on each render because you build its `value` inline.";
21002
+ const MESSAGE$47 = "Every reader of this context redraws on each render because you build its `value` inline.";
20251
21003
  const CONTEXT_MODULES$1 = [
20252
21004
  "react",
20253
21005
  "use-context-selector",
@@ -20345,7 +21097,7 @@ const jsxNoConstructedContextValues = defineRule({
20345
21097
  if (!isConstructedValue(innerExpression)) continue;
20346
21098
  context.report({
20347
21099
  node: attribute,
20348
- message: MESSAGE$46
21100
+ message: MESSAGE$47
20349
21101
  });
20350
21102
  }
20351
21103
  }
@@ -21200,7 +21952,7 @@ const DATA_ARRAY_PROP_SUFFIXES = [
21200
21952
  ];
21201
21953
  //#endregion
21202
21954
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop.ts
21203
- const MESSAGE$45 = "This child redraws every render because the prop gets a brand new array each time.";
21955
+ const MESSAGE$46 = "This child redraws every render because the prop gets a brand new array each time.";
21204
21956
  const isDataArrayPropName = (propName) => {
21205
21957
  if (DATA_ARRAY_PROP_NAMES.has(propName)) return true;
21206
21958
  for (const suffix of DATA_ARRAY_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -21287,7 +22039,7 @@ const jsxNoNewArrayAsProp = defineRule({
21287
22039
  if (!isArrayProducingExpression(expressionNode) && !followsRenderLocalArrayBinding(expressionNode, node)) return;
21288
22040
  context.report({
21289
22041
  node,
21290
- message: MESSAGE$45
22042
+ message: MESSAGE$46
21291
22043
  });
21292
22044
  }
21293
22045
  };
@@ -21545,7 +22297,7 @@ const SAFE_RECEIVER_NAMES = new Set([
21545
22297
  ]);
21546
22298
  //#endregion
21547
22299
  //#region src/plugin/rules/react-builtins/jsx-no-new-function-as-prop.ts
21548
- const MESSAGE$44 = "This child redraws every render because the prop gets a brand new function each time.";
22300
+ const MESSAGE$45 = "This child redraws every render because the prop gets a brand new function each time.";
21549
22301
  const isAccessorPredicateName = (propName) => {
21550
22302
  for (const prefix of ACCESSOR_PREDICATE_PREFIXES) {
21551
22303
  if (propName.length <= prefix.length) continue;
@@ -21751,7 +22503,7 @@ const jsxNoNewFunctionAsProp = defineRule({
21751
22503
  if (!isFunctionProducingExpression(expressionNode) && !followsRenderLocalFunctionBinding(expressionNode, node)) return;
21752
22504
  context.report({
21753
22505
  node,
21754
- message: MESSAGE$44
22506
+ message: MESSAGE$45
21755
22507
  });
21756
22508
  }
21757
22509
  };
@@ -21971,7 +22723,7 @@ const CONFIG_OBJECT_PROP_SUFFIXES = [
21971
22723
  ];
21972
22724
  //#endregion
21973
22725
  //#region src/plugin/rules/react-builtins/jsx-no-new-object-as-prop.ts
21974
- const MESSAGE$43 = "This child redraws every render because the prop gets a brand new object each time.";
22726
+ const MESSAGE$44 = "This child redraws every render because the prop gets a brand new object each time.";
21975
22727
  const isConfigObjectPropName = (propName) => {
21976
22728
  if (CONFIG_OBJECT_PROP_NAMES.has(propName)) return true;
21977
22729
  for (const suffix of CONFIG_OBJECT_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -22060,7 +22812,7 @@ const jsxNoNewObjectAsProp = defineRule({
22060
22812
  if (!isObjectProducingExpression(expressionNode) && !followsRenderLocalObjectBinding(expressionNode, node)) return;
22061
22813
  context.report({
22062
22814
  node,
22063
- message: MESSAGE$43
22815
+ message: MESSAGE$44
22064
22816
  });
22065
22817
  }
22066
22818
  };
@@ -22068,7 +22820,7 @@ const jsxNoNewObjectAsProp = defineRule({
22068
22820
  });
22069
22821
  //#endregion
22070
22822
  //#region src/plugin/rules/react-builtins/jsx-no-script-url.ts
22071
- const MESSAGE$42 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
22823
+ const MESSAGE$43 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
22072
22824
  const JAVASCRIPT_URL_PATTERN = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
22073
22825
  const resolveSettings$29 = (settings) => {
22074
22826
  const reactDoctor = settings?.["react-doctor"];
@@ -22106,7 +22858,7 @@ const jsxNoScriptUrl = defineRule({
22106
22858
  if (!value || !isNodeOfType(value, "Literal") || typeof value.value !== "string") continue;
22107
22859
  if (JAVASCRIPT_URL_PATTERN.test(value.value)) context.report({
22108
22860
  node: attribute,
22109
- message: MESSAGE$42
22861
+ message: MESSAGE$43
22110
22862
  });
22111
22863
  }
22112
22864
  } };
@@ -22726,7 +23478,7 @@ const jsxPropsNoSpreadMulti = defineRule({
22726
23478
  });
22727
23479
  //#endregion
22728
23480
  //#region src/plugin/rules/react-builtins/jsx-props-no-spreading.ts
22729
- const MESSAGE$41 = "You can't tell what props reach this element when you spread them.";
23481
+ const MESSAGE$42 = "You can't tell what props reach this element when you spread them.";
22730
23482
  const resolveSettings$25 = (settings) => {
22731
23483
  const reactDoctor = settings?.["react-doctor"];
22732
23484
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxPropsNoSpreading ?? {} : {};
@@ -22769,7 +23521,7 @@ const jsxPropsNoSpreading = defineRule({
22769
23521
  }
22770
23522
  context.report({
22771
23523
  node: attribute,
22772
- message: MESSAGE$41
23524
+ message: MESSAGE$42
22773
23525
  });
22774
23526
  didReportInFile = true;
22775
23527
  return;
@@ -23045,7 +23797,7 @@ const labelHasAssociatedControl = defineRule({
23045
23797
  });
23046
23798
  //#endregion
23047
23799
  //#region src/plugin/rules/a11y/lang.ts
23048
- const MESSAGE$40 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
23800
+ const MESSAGE$41 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
23049
23801
  const COMMON_LANGUAGE_PRIMARY_TAGS = new Set([
23050
23802
  "aa",
23051
23803
  "ab",
@@ -23525,7 +24277,7 @@ const lang = defineRule({
23525
24277
  if (expression.type === "Identifier" && expression.name === "undefined" || expression.type === "Literal" && expression.value === null) {
23526
24278
  context.report({
23527
24279
  node: langAttr,
23528
- message: MESSAGE$40
24280
+ message: MESSAGE$41
23529
24281
  });
23530
24282
  return;
23531
24283
  }
@@ -23534,7 +24286,7 @@ const lang = defineRule({
23534
24286
  if (value === null) return;
23535
24287
  if (!isValidLangTag(value)) context.report({
23536
24288
  node: langAttr,
23537
- message: MESSAGE$40
24289
+ message: MESSAGE$41
23538
24290
  });
23539
24291
  } })
23540
24292
  });
@@ -23585,7 +24337,7 @@ const mdxSsrExecutionRisk = defineRule({
23585
24337
  });
23586
24338
  //#endregion
23587
24339
  //#region src/plugin/rules/a11y/media-has-caption.ts
23588
- const MESSAGE$39 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
24340
+ const MESSAGE$40 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
23589
24341
  const DEFAULT_AUDIO = ["audio"];
23590
24342
  const DEFAULT_VIDEO = ["video"];
23591
24343
  const DEFAULT_TRACK = ["track"];
@@ -23674,7 +24426,7 @@ const mediaHasCaption = defineRule({
23674
24426
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
23675
24427
  context.report({
23676
24428
  node: node.name,
23677
- message: MESSAGE$39
24429
+ message: MESSAGE$40
23678
24430
  });
23679
24431
  return;
23680
24432
  }
@@ -23693,7 +24445,7 @@ const mediaHasCaption = defineRule({
23693
24445
  return kindValue.value.toLowerCase() === "captions";
23694
24446
  })) context.report({
23695
24447
  node: node.name,
23696
- message: MESSAGE$39
24448
+ message: MESSAGE$40
23697
24449
  });
23698
24450
  } };
23699
24451
  }
@@ -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",
@@ -25236,7 +25962,7 @@ const nextjsNoVercelOgImport = defineRule({
25236
25962
  });
25237
25963
  //#endregion
25238
25964
  //#region src/plugin/rules/a11y/no-access-key.ts
25239
- const MESSAGE$38 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25965
+ const MESSAGE$39 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25240
25966
  const isUndefinedIdentifier = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
25241
25967
  const noAccessKey = defineRule({
25242
25968
  id: "no-access-key",
@@ -25255,7 +25981,7 @@ const noAccessKey = defineRule({
25255
25981
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
25256
25982
  context.report({
25257
25983
  node: accessKey,
25258
- message: MESSAGE$38
25984
+ message: MESSAGE$39
25259
25985
  });
25260
25986
  return;
25261
25987
  }
@@ -25265,7 +25991,7 @@ const noAccessKey = defineRule({
25265
25991
  if (isUndefinedIdentifier(expression)) return;
25266
25992
  context.report({
25267
25993
  node: accessKey,
25268
- message: MESSAGE$38
25994
+ message: MESSAGE$39
25269
25995
  });
25270
25996
  }
25271
25997
  } };
@@ -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;
@@ -27120,7 +27846,7 @@ const noAdjustStateOnPropChange = defineRule({
27120
27846
  });
27121
27847
  //#endregion
27122
27848
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
27123
- const MESSAGE$37 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
27849
+ const MESSAGE$38 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
27124
27850
  const ALWAYS_FOCUSABLE_TAGS = new Set([
27125
27851
  "button",
27126
27852
  "embed",
@@ -27232,7 +27958,7 @@ const noAriaHiddenOnFocusable = defineRule({
27232
27958
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
27233
27959
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
27234
27960
  node: ariaHidden,
27235
- message: MESSAGE$37
27961
+ message: MESSAGE$38
27236
27962
  });
27237
27963
  } })
27238
27964
  });
@@ -28154,7 +28880,7 @@ const isAllLiteralArrayExpression = (node) => {
28154
28880
  };
28155
28881
  //#endregion
28156
28882
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
28157
- const MESSAGE$36 = "Your users can see & submit the wrong data when this list reorders.";
28883
+ const MESSAGE$37 = "Your users can see & submit the wrong data when this list reorders.";
28158
28884
  const SECOND_INDEX_METHODS = new Set([
28159
28885
  "every",
28160
28886
  "filter",
@@ -28273,14 +28999,14 @@ const noArrayIndexKey = defineRule({
28273
28999
  if (propName !== "key") continue;
28274
29000
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
28275
29001
  node: property,
28276
- message: MESSAGE$36
29002
+ message: MESSAGE$37
28277
29003
  });
28278
29004
  }
28279
29005
  } })
28280
29006
  });
28281
29007
  //#endregion
28282
29008
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
28283
- const MESSAGE$35 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
29009
+ const MESSAGE$36 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
28284
29010
  const noAsyncEffectCallback = defineRule({
28285
29011
  id: "no-async-effect-callback",
28286
29012
  title: "Async effect callback",
@@ -28298,13 +29024,13 @@ const noAsyncEffectCallback = defineRule({
28298
29024
  if (!callback.async) return;
28299
29025
  context.report({
28300
29026
  node: callback,
28301
- message: MESSAGE$35
29027
+ message: MESSAGE$36
28302
29028
  });
28303
29029
  } })
28304
29030
  });
28305
29031
  //#endregion
28306
29032
  //#region src/plugin/rules/a11y/no-autofocus.ts
28307
- const MESSAGE$34 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
29033
+ const MESSAGE$35 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
28308
29034
  const resolveSettings$21 = (settings) => {
28309
29035
  const reactDoctor = settings?.["react-doctor"];
28310
29036
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -28401,7 +29127,7 @@ const noAutofocus = defineRule({
28401
29127
  if (isConditionallyRendered(node)) return;
28402
29128
  context.report({
28403
29129
  node: autoFocusAttribute,
28404
- message: MESSAGE$34
29130
+ message: MESSAGE$35
28405
29131
  });
28406
29132
  } };
28407
29133
  }
@@ -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;
@@ -29374,7 +29968,7 @@ const noChainStateUpdates = defineRule({
29374
29968
  });
29375
29969
  //#endregion
29376
29970
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
29377
- const MESSAGE$33 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29971
+ const MESSAGE$34 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29378
29972
  const noChildrenProp = defineRule({
29379
29973
  id: "no-children-prop",
29380
29974
  title: "Children passed as a prop",
@@ -29386,7 +29980,7 @@ const noChildrenProp = defineRule({
29386
29980
  if (node.name.name !== "children") return;
29387
29981
  context.report({
29388
29982
  node: node.name,
29389
- message: MESSAGE$33
29983
+ message: MESSAGE$34
29390
29984
  });
29391
29985
  },
29392
29986
  CallExpression(node) {
@@ -29399,7 +29993,7 @@ const noChildrenProp = defineRule({
29399
29993
  const propertyKey = property.key;
29400
29994
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
29401
29995
  node: propertyKey,
29402
- message: MESSAGE$33
29996
+ message: MESSAGE$34
29403
29997
  });
29404
29998
  }
29405
29999
  }
@@ -29407,7 +30001,7 @@ const noChildrenProp = defineRule({
29407
30001
  });
29408
30002
  //#endregion
29409
30003
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
29410
- const MESSAGE$32 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
30004
+ const MESSAGE$33 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29411
30005
  const noCloneElement = defineRule({
29412
30006
  id: "no-clone-element",
29413
30007
  title: "cloneElement makes child props fragile",
@@ -29420,7 +30014,7 @@ const noCloneElement = defineRule({
29420
30014
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
29421
30015
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
29422
30016
  node: callee,
29423
- message: MESSAGE$32
30017
+ message: MESSAGE$33
29424
30018
  });
29425
30019
  return;
29426
30020
  }
@@ -29433,7 +30027,7 @@ const noCloneElement = defineRule({
29433
30027
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
29434
30028
  context.report({
29435
30029
  node: callee,
29436
- message: MESSAGE$32
30030
+ message: MESSAGE$33
29437
30031
  });
29438
30032
  }
29439
30033
  } })
@@ -29453,7 +30047,7 @@ const getCallMethodName = (callee) => {
29453
30047
  };
29454
30048
  //#endregion
29455
30049
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
29456
- const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
30050
+ const MESSAGE$32 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29457
30051
  const CONTEXT_MODULES = [
29458
30052
  "react",
29459
30053
  "use-context-selector",
@@ -29501,13 +30095,13 @@ const noCreateContextInRender = defineRule({
29501
30095
  if (!componentOrHookName) return;
29502
30096
  context.report({
29503
30097
  node,
29504
- message: `${MESSAGE$31} (called inside "${componentOrHookName}")`
30098
+ message: `${MESSAGE$32} (called inside "${componentOrHookName}")`
29505
30099
  });
29506
30100
  } })
29507
30101
  });
29508
30102
  //#endregion
29509
30103
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
29510
- const MESSAGE$30 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
30104
+ const MESSAGE$31 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
29511
30105
  const isUseMemoCallbackArgument = (functionNode) => {
29512
30106
  const parent = functionNode.parent;
29513
30107
  if (!parent || !isNodeOfType(parent, "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({
@@ -29537,7 +30131,7 @@ const noCreateRefInFunctionComponent = defineRule({
29537
30131
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes, context.cfg))) return;
29538
30132
  context.report({
29539
30133
  node,
29540
- message: MESSAGE$30
30134
+ message: MESSAGE$31
29541
30135
  });
29542
30136
  } })
29543
30137
  });
@@ -29677,7 +30271,7 @@ const noCreateStoreInRender = defineRule({
29677
30271
  });
29678
30272
  //#endregion
29679
30273
  //#region src/plugin/rules/react-builtins/no-danger.ts
29680
- const MESSAGE$29 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
30274
+ const MESSAGE$30 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
29681
30275
  const noDanger = defineRule({
29682
30276
  id: "no-danger",
29683
30277
  title: "Raw HTML injection can run unsafe markup",
@@ -29691,7 +30285,7 @@ const noDanger = defineRule({
29691
30285
  if (!propAttribute) return;
29692
30286
  context.report({
29693
30287
  node: propAttribute.name,
29694
- message: MESSAGE$29
30288
+ message: MESSAGE$30
29695
30289
  });
29696
30290
  },
29697
30291
  CallExpression(node) {
@@ -29703,7 +30297,7 @@ const noDanger = defineRule({
29703
30297
  const propertyKey = property.key;
29704
30298
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
29705
30299
  node: propertyKey,
29706
- message: MESSAGE$29
30300
+ message: MESSAGE$30
29707
30301
  });
29708
30302
  }
29709
30303
  }
@@ -29726,7 +30320,7 @@ const isMeaningfulJsxChild = (child) => {
29726
30320
  };
29727
30321
  //#endregion
29728
30322
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
29729
- const MESSAGE$28 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
30323
+ const MESSAGE$29 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
29730
30324
  const mergePropsShape = (target, source) => {
29731
30325
  target.hasDangerously ||= source.hasDangerously;
29732
30326
  target.hasChildren ||= source.hasChildren;
@@ -29801,7 +30395,7 @@ const noDangerWithChildren = defineRule({
29801
30395
  if (!hasChildrenProp && !hasNestedChildren) return;
29802
30396
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
29803
30397
  node: opening,
29804
- message: MESSAGE$28
30398
+ message: MESSAGE$29
29805
30399
  });
29806
30400
  },
29807
30401
  CallExpression(node) {
@@ -29814,7 +30408,7 @@ const noDangerWithChildren = defineRule({
29814
30408
  const positionalChildren = node.arguments.slice(2);
29815
30409
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
29816
30410
  node,
29817
- message: MESSAGE$28
30411
+ message: MESSAGE$29
29818
30412
  });
29819
30413
  }
29820
30414
  })
@@ -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;
@@ -30779,7 +31373,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
30779
31373
  //#endregion
30780
31374
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
30781
31375
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
30782
- const MESSAGE$27 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
31376
+ const MESSAGE$28 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
30783
31377
  const getNodeStart = (node) => "start" in node && typeof node.start === "number" ? node.start : -1;
30784
31378
  const getEnclosingLifecycleFunction = (setStateCall) => {
30785
31379
  let ancestor = setStateCall.parent;
@@ -30907,7 +31501,7 @@ const noDidMountSetState = defineRule({
30907
31501
  }
30908
31502
  context.report({
30909
31503
  node: node.callee,
30910
- message: MESSAGE$27
31504
+ message: MESSAGE$28
30911
31505
  });
30912
31506
  } };
30913
31507
  }
@@ -30915,7 +31509,7 @@ const noDidMountSetState = defineRule({
30915
31509
  //#endregion
30916
31510
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
30917
31511
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
30918
- const MESSAGE$26 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
31512
+ const MESSAGE$27 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
30919
31513
  const EQUALITY_OPERATORS = new Set([
30920
31514
  "==",
30921
31515
  "===",
@@ -31089,7 +31683,7 @@ const noDidUpdateSetState = defineRule({
31089
31683
  if (isInsideDiffGuard(node)) return;
31090
31684
  context.report({
31091
31685
  node: node.callee,
31092
- message: MESSAGE$26
31686
+ message: MESSAGE$27
31093
31687
  });
31094
31688
  } };
31095
31689
  }
@@ -31112,7 +31706,7 @@ const isStateMemberExpression = (node) => {
31112
31706
  };
31113
31707
  //#endregion
31114
31708
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
31115
- const MESSAGE$25 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
31709
+ const MESSAGE$26 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
31116
31710
  const shouldIgnoreMutation = (node) => {
31117
31711
  let isConstructor = false;
31118
31712
  let isInsideCallExpression = false;
@@ -31134,7 +31728,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
31134
31728
  if (shouldIgnoreMutation(reportNode)) return;
31135
31729
  context.report({
31136
31730
  node: reportNode,
31137
- message: MESSAGE$25
31731
+ message: MESSAGE$26
31138
31732
  });
31139
31733
  };
31140
31734
  const noDirectMutationState = defineRule({
@@ -31485,7 +32079,7 @@ const noDocumentStartViewTransition = defineRule({
31485
32079
  });
31486
32080
  //#endregion
31487
32081
  //#region src/plugin/rules/js-performance/no-document-write.ts
31488
- const MESSAGE$24 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
32082
+ const MESSAGE$25 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
31489
32083
  const WRITE_METHODS = new Set(["write", "writeln"]);
31490
32084
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
31491
32085
  const noDocumentWrite = defineRule({
@@ -31502,7 +32096,7 @@ const noDocumentWrite = defineRule({
31502
32096
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
31503
32097
  context.report({
31504
32098
  node,
31505
- message: MESSAGE$24
32099
+ message: MESSAGE$25
31506
32100
  });
31507
32101
  } })
31508
32102
  });
@@ -32555,7 +33149,7 @@ const isFunctionUsedOutsideHandlers = (analysis, functionNode, componentFunction
32555
33149
  if (isInsideInlineEventHandler(identifier, componentFunction)) return false;
32556
33150
  const parent = identifier.parent;
32557
33151
  if (parent && isNodeOfType(parent, "CallExpression") && parent.callee === identifier) {
32558
- if (findEnclosingFunction(parent) === functionNode) return false;
33152
+ if (findEnclosingFunction$1(parent) === functionNode) return false;
32559
33153
  if (isInsideProvenEventHandler(analysis, parent, componentFunction, false)) return false;
32560
33154
  }
32561
33155
  return true;
@@ -32601,7 +33195,7 @@ const isStateWrittenOnlyFromEventHandlers = (analysis, stateReference) => {
32601
33195
  if (!setterVariable) return false;
32602
33196
  const stateDeclarator = getUseStateDecl(analysis, stateReference);
32603
33197
  if (!stateDeclarator) return false;
32604
- const componentFunction = findEnclosingFunction(stateDeclarator);
33198
+ const componentFunction = findEnclosingFunction$1(stateDeclarator);
32605
33199
  if (!componentFunction) return false;
32606
33200
  let hasWriter = false;
32607
33201
  for (const reference of setterVariable.references) {
@@ -33663,8 +34257,8 @@ const isAwaitedInFunction = (request, functionNode) => {
33663
34257
  return false;
33664
34258
  };
33665
34259
  const isCompletionSinkForRequest = (completionSink, request) => {
33666
- const requestFunction = findEnclosingFunction(request);
33667
- const completionSinkFunction = findEnclosingFunction(completionSink);
34260
+ const requestFunction = findEnclosingFunction$1(request);
34261
+ const completionSinkFunction = findEnclosingFunction$1(completionSink);
33668
34262
  if (!requestFunction || !completionSinkFunction) return false;
33669
34263
  if (requestFunction === completionSinkFunction) {
33670
34264
  if (!isAwaitedInFunction(request, requestFunction)) return false;
@@ -33720,7 +34314,7 @@ const ALLOWED_NAMESPACES = new Set([
33720
34314
  "ReactDOM",
33721
34315
  "ReactDom"
33722
34316
  ]);
33723
- const MESSAGE$23 = "`findDOMNode` crashes your app in React 19 because it was removed.";
34317
+ const MESSAGE$24 = "`findDOMNode` crashes your app in React 19 because it was removed.";
33724
34318
  const noFindDomNode = defineRule({
33725
34319
  id: "no-find-dom-node",
33726
34320
  title: "findDOMNode breaks component encapsulation",
@@ -33731,7 +34325,7 @@ const noFindDomNode = defineRule({
33731
34325
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
33732
34326
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
33733
34327
  node: callee,
33734
- message: MESSAGE$23
34328
+ message: MESSAGE$24
33735
34329
  });
33736
34330
  return;
33737
34331
  }
@@ -33742,7 +34336,7 @@ const noFindDomNode = defineRule({
33742
34336
  if (callee.property.name !== "findDOMNode") return;
33743
34337
  context.report({
33744
34338
  node: callee.property,
33745
- message: MESSAGE$23
34339
+ message: MESSAGE$24
33746
34340
  });
33747
34341
  }
33748
34342
  } })
@@ -33951,13 +34545,6 @@ const isPublishedLibraryPackage = (filename) => {
33951
34545
  return manifest.private !== true && typeof manifest.peerDependencies === "object" && manifest.peerDependencies !== null && "react" in manifest.peerDependencies;
33952
34546
  };
33953
34547
  //#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
34548
  //#region src/plugin/rules/bundle-size/no-full-lodash-import.ts
33962
34549
  const isLibraryDevPage = (filename) => {
33963
34550
  if (!filename) return false;
@@ -34521,7 +35108,7 @@ const isInRenderedOutput = (node, componentOrHookNode, scopes) => {
34521
35108
  return attribute ? !isEventHandlerAttribute(attribute) : true;
34522
35109
  }
34523
35110
  if (isNodeOfType(parentNode, "ReturnStatement")) {
34524
- if (findEnclosingFunction(parentNode) === componentOrHookNode) return true;
35111
+ if (findEnclosingFunction$1(parentNode) === componentOrHookNode) return true;
34525
35112
  }
34526
35113
  if (parentNode === componentOrHookNode) return isFunctionLike$2(componentOrHookNode) && !isNodeOfType(componentOrHookNode.body, "BlockStatement") && componentOrHookNode.body === currentNode;
34527
35114
  if (isFunctionLike$2(parentNode) && !executesDuringRender(parentNode, scopes)) return false;
@@ -34644,7 +35231,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
34644
35231
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
34645
35232
  const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
34646
35233
  if (!componentOrHookNode) return;
34647
- const enclosingFunction = findEnclosingFunction(node);
35234
+ const enclosingFunction = findEnclosingFunction$1(node);
34648
35235
  if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
34649
35236
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
34650
35237
  if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
@@ -34656,7 +35243,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
34656
35243
  });
34657
35244
  //#endregion
34658
35245
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
34659
- const MESSAGE$22 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
35246
+ const MESSAGE$23 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
34660
35247
  const noImgLazyWithHighFetchpriority = defineRule({
34661
35248
  id: "no-img-lazy-with-high-fetchpriority",
34662
35249
  title: "Lazy image with high fetchPriority",
@@ -34670,7 +35257,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
34670
35257
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
34671
35258
  context.report({
34672
35259
  node: node.name,
34673
- message: MESSAGE$22
35260
+ message: MESSAGE$23
34674
35261
  });
34675
35262
  } })
34676
35263
  });
@@ -34859,7 +35446,7 @@ const noImpureStateUpdater = defineRule({
34859
35446
  });
34860
35447
  //#endregion
34861
35448
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
34862
- const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
35449
+ const MESSAGE$22 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
34863
35450
  const REACT_USE_REF_OPTIONS = {
34864
35451
  allowGlobalReactNamespace: false,
34865
35452
  allowUnboundBareCalls: false
@@ -34960,7 +35547,7 @@ const noIndeterminateAttribute = defineRule({
34960
35547
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
34961
35548
  context.report({
34962
35549
  node: indeterminateAttribute,
34963
- message: MESSAGE$21
35550
+ message: MESSAGE$22
34964
35551
  });
34965
35552
  },
34966
35553
  CallExpression(node) {
@@ -34969,7 +35556,7 @@ const noIndeterminateAttribute = defineRule({
34969
35556
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
34970
35557
  context.report({
34971
35558
  node,
34972
- message: MESSAGE$21
35559
+ message: MESSAGE$22
34973
35560
  });
34974
35561
  }
34975
35562
  };
@@ -35092,10 +35679,10 @@ const isInsideInstanceField = (node) => {
35092
35679
  return false;
35093
35680
  };
35094
35681
  const isOneShotModuleInitialization = (node, context) => {
35095
- let functionNode = findEnclosingFunction(node);
35682
+ let functionNode = findEnclosingFunction$1(node);
35096
35683
  while (functionNode) {
35097
35684
  if (!executesDuringRender(functionNode, context.scopes)) return false;
35098
- functionNode = findEnclosingFunction(functionNode);
35685
+ functionNode = findEnclosingFunction$1(functionNode);
35099
35686
  }
35100
35687
  return !isInsideInstanceField(node);
35101
35688
  };
@@ -35257,7 +35844,7 @@ const noIsMounted = defineRule({
35257
35844
  });
35258
35845
  //#endregion
35259
35846
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
35260
- const MESSAGE$20 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
35847
+ const MESSAGE$21 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
35261
35848
  const isJsonMethodCall = (node, method) => {
35262
35849
  if (!isNodeOfType(node, "CallExpression")) return false;
35263
35850
  const callee = node.callee;
@@ -35321,13 +35908,13 @@ const noJsonParseStringifyClone = defineRule({
35321
35908
  if (isCatchParameterRoundTrip(firstArgument)) return;
35322
35909
  context.report({
35323
35910
  node,
35324
- message: MESSAGE$20
35911
+ message: MESSAGE$21
35325
35912
  });
35326
35913
  } })
35327
35914
  });
35328
35915
  //#endregion
35329
35916
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
35330
- const MESSAGE$19 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35917
+ const MESSAGE$20 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35331
35918
  const isJsxElementTypeReference = (node) => {
35332
35919
  if (!isNodeOfType(node, "TSTypeReference")) return false;
35333
35920
  const typeName = node.typeName;
@@ -35381,7 +35968,7 @@ const noJsxElementType = defineRule({
35381
35968
  if (isJsxImported) return;
35382
35969
  for (const typeAnnotation of flaggedAnnotations) context.report({
35383
35970
  node: typeAnnotation,
35384
- message: MESSAGE$19
35971
+ message: MESSAGE$20
35385
35972
  });
35386
35973
  }
35387
35974
  };
@@ -35613,6 +36200,221 @@ const noLegacyClassLifecycles = defineRule({
35613
36200
  }
35614
36201
  });
35615
36202
  //#endregion
36203
+ //#region src/plugin/utils/is-proven-react-class-component.ts
36204
+ const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
36205
+ const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
36206
+ const expression = stripParenExpression(node);
36207
+ if (isNodeOfType(expression, "MemberExpression")) {
36208
+ const propertyName = getStaticPropertyName(expression);
36209
+ const receiver = stripParenExpression(expression.object);
36210
+ return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
36211
+ }
36212
+ if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
36213
+ if (!isNodeOfType(expression, "Identifier")) return false;
36214
+ const symbol = scopes.symbolFor(expression);
36215
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
36216
+ visitedSymbolIds.add(symbol.id);
36217
+ if (isImportedFromReact(symbol)) {
36218
+ const importedName = getImportedName(symbol.declarationNode);
36219
+ return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
36220
+ }
36221
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
36222
+ return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
36223
+ };
36224
+ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36225
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
36226
+ visitedClassNodes.add(classNode);
36227
+ return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
36228
+ };
36229
+ //#endregion
36230
+ //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
36231
+ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
36232
+ if (!isFunctionLike$2(functionNode)) return false;
36233
+ let containsReactHookCall = false;
36234
+ walkAst(functionNode.body, (node) => {
36235
+ if (containsReactHookCall) return false;
36236
+ if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
36237
+ if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
36238
+ containsReactHookCall = true;
36239
+ return false;
36240
+ }
36241
+ });
36242
+ return containsReactHookCall;
36243
+ };
36244
+ //#endregion
36245
+ //#region src/plugin/utils/function-returns-props-children.ts
36246
+ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
36247
+ if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
36248
+ const firstParameter = stripParenExpression(functionNode.params[0]);
36249
+ const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
36250
+ const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
36251
+ const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
36252
+ if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
36253
+ for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
36254
+ const propertyValue = stripParenExpression(property.value);
36255
+ const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
36256
+ if (!isNodeOfType(childrenBinding, "Identifier")) continue;
36257
+ const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
36258
+ if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
36259
+ }
36260
+ }
36261
+ return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
36262
+ const candidate = stripParenExpression(expression);
36263
+ if (isNodeOfType(candidate, "Identifier")) {
36264
+ const symbol = scopes.symbolFor(candidate);
36265
+ return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
36266
+ }
36267
+ if (!isNodeOfType(candidate, "MemberExpression")) return false;
36268
+ if (getStaticPropertyName(candidate) !== "children") return false;
36269
+ const receiver = stripParenExpression(candidate.object);
36270
+ if (!isNodeOfType(receiver, "Identifier")) return false;
36271
+ const receiverSymbol = scopes.symbolFor(receiver);
36272
+ if (!receiverSymbol || !propsParameterSymbol) return false;
36273
+ return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
36274
+ }, controlFlow);
36275
+ };
36276
+ //#endregion
36277
+ //#region src/plugin/utils/function-returns-only-null.ts
36278
+ const isNullExpression = (expression) => {
36279
+ const candidate = stripParenExpression(expression);
36280
+ return isNodeOfType(candidate, "Literal") && candidate.value === null;
36281
+ };
36282
+ const functionReturnsOnlyNull = (functionNode) => {
36283
+ if (!isFunctionLike$2(functionNode)) return false;
36284
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
36285
+ const returnStatements = collectFunctionReturnStatements(functionNode);
36286
+ return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
36287
+ };
36288
+ //#endregion
36289
+ //#region src/plugin/utils/is-proven-styled-component-expression.ts
36290
+ const findFactoryRoot = (node) => {
36291
+ const candidate = stripParenExpression(node);
36292
+ if (isNodeOfType(candidate, "Identifier")) return candidate;
36293
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
36294
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
36295
+ return null;
36296
+ };
36297
+ const findFactoryPropertyName = (node) => {
36298
+ const candidate = stripParenExpression(node);
36299
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
36300
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
36301
+ return null;
36302
+ };
36303
+ const isProvenStyledComponentExpression = (expression, scopes) => {
36304
+ const candidate = stripParenExpression(expression);
36305
+ if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
36306
+ const factoryRoot = findFactoryRoot(candidate.tag);
36307
+ if (!factoryRoot) return false;
36308
+ const factoryPropertyName = findFactoryPropertyName(candidate.tag);
36309
+ if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
36310
+ const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
36311
+ if (!symbol || symbol.kind !== "import") return false;
36312
+ if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
36313
+ const importDeclaration = symbol.declarationNode.parent;
36314
+ return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
36315
+ };
36316
+ //#endregion
36317
+ //#region src/plugin/utils/is-proven-react-component-symbol.ts
36318
+ const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
36319
+ const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
36320
+ const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
36321
+ const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36322
+ const candidate = stripParenExpression(expression);
36323
+ if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
36324
+ if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
36325
+ if (isProvenStyledComponentExpression(candidate, scopes)) return true;
36326
+ if (isNodeOfType(candidate, "Identifier")) {
36327
+ const symbol = scopes.symbolFor(candidate);
36328
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
36329
+ visitedSymbolIds.add(symbol.id);
36330
+ if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
36331
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
36332
+ return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
36333
+ }
36334
+ if (!isNodeOfType(candidate, "CallExpression")) return false;
36335
+ if (!hasStableCallTarget(candidate, scopes)) return false;
36336
+ const factoryCallee = stripParenExpression(candidate.callee);
36337
+ if (isNodeOfType(factoryCallee, "Identifier") && isDefaultImportFromModule(factoryCallee, factoryCallee.name, "create-react-class") || isReactApiCall(candidate, LEGACY_REACT_COMPONENT_FACTORY_NAMES, scopes)) return true;
36338
+ if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
36339
+ const wrappedComponent = candidate.arguments[0];
36340
+ return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
36341
+ }
36342
+ if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
36343
+ const factory = candidate.arguments[0];
36344
+ if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
36345
+ const unwrappedFactory = stripParenExpression(factory);
36346
+ if (!isInlineFunctionExpression(unwrappedFactory)) return false;
36347
+ if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
36348
+ const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
36349
+ const returnedExpression = returnStatements[0]?.argument;
36350
+ return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
36351
+ };
36352
+ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
36353
+ const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
36354
+ for (const candidateSymbol of candidateSymbols) {
36355
+ if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
36356
+ if (isComponentDeclaration(candidateSymbol.declarationNode)) {
36357
+ if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
36358
+ continue;
36359
+ }
36360
+ const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
36361
+ if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
36362
+ if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
36363
+ continue;
36364
+ }
36365
+ if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
36366
+ if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
36367
+ continue;
36368
+ }
36369
+ if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
36370
+ }
36371
+ return false;
36372
+ };
36373
+ //#endregion
36374
+ //#region src/plugin/utils/symbol-has-react-component-type-annotation.ts
36375
+ const REACT_COMPONENT_TYPE_NAMES = new Set([
36376
+ "ComponentClass",
36377
+ "ComponentType",
36378
+ "FC",
36379
+ "FunctionComponent"
36380
+ ]);
36381
+ const findVisibleSymbol = (identifier, scopes) => {
36382
+ if (!isNodeOfType(identifier, "Identifier")) return null;
36383
+ let scope = scopes.scopeFor(identifier);
36384
+ while (true) {
36385
+ const symbol = scope.symbolsByName.get(identifier.name);
36386
+ if (symbol) return symbol;
36387
+ if (!scope.parent) return null;
36388
+ scope = scope.parent;
36389
+ }
36390
+ };
36391
+ const isReactNamespaceType = (identifier, scopes) => {
36392
+ const symbol = findVisibleSymbol(identifier, scopes);
36393
+ return Boolean(symbol && isImportedFromReact(symbol) && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
36394
+ };
36395
+ const isReactComponentType = (typeNode, scopes, visitedSymbolIds) => {
36396
+ if (!typeNode) return false;
36397
+ if (isNodeOfType(typeNode, "TSTypeAnnotation")) return isReactComponentType(typeNode.typeAnnotation, scopes, visitedSymbolIds);
36398
+ if (isNodeOfType(typeNode, "TSIntersectionType")) return (typeNode.types ?? []).some((member) => isReactComponentType(member, scopes, visitedSymbolIds));
36399
+ if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
36400
+ const typeName = typeNode.typeName;
36401
+ if (isNodeOfType(typeName, "TSQualifiedName")) return isNodeOfType(typeName.left, "Identifier") && isNodeOfType(typeName.right, "Identifier") && REACT_COMPONENT_TYPE_NAMES.has(typeName.right.name) && isReactNamespaceType(typeName.left, scopes);
36402
+ if (!isNodeOfType(typeName, "Identifier")) return false;
36403
+ const typeSymbol = findVisibleSymbol(typeName, scopes);
36404
+ if (!typeSymbol || visitedSymbolIds.has(typeSymbol.id)) return false;
36405
+ if (isImportedFromReact(typeSymbol)) {
36406
+ const importedName = getImportedName(typeSymbol.declarationNode);
36407
+ return Boolean(importedName && REACT_COMPONENT_TYPE_NAMES.has(importedName));
36408
+ }
36409
+ if (!isNodeOfType(typeSymbol.declarationNode, "TSTypeAliasDeclaration")) return false;
36410
+ visitedSymbolIds.add(typeSymbol.id);
36411
+ return isReactComponentType(typeSymbol.declarationNode.typeAnnotation, scopes, visitedSymbolIds);
36412
+ };
36413
+ const symbolHasReactComponentTypeAnnotation = (symbol, scopes) => {
36414
+ const bindingIdentifier = symbol.bindingIdentifier;
36415
+ return isNodeOfType(bindingIdentifier, "Identifier") && isReactComponentType(bindingIdentifier.typeAnnotation, scopes, /* @__PURE__ */ new Set());
36416
+ };
36417
+ //#endregion
35616
36418
  //#region src/plugin/rules/architecture/no-legacy-context-api.ts
35617
36419
  const LEGACY_CONTEXT_NAMES = new Set([
35618
36420
  "childContextTypes",
@@ -35623,15 +36425,6 @@ const buildLegacyContextMessage = (memberName) => {
35623
36425
  if (memberName === "childContextTypes" || memberName === "getChildContext") return `${memberName} uses the old context API that React 19 removes, so your provider stops passing data. Switch to \`createContext\` with \`<MyContext.Provider value={...}>\` & read it with \`useContext()\`, moving every consumer together.`;
35624
36426
  return "contextTypes uses the old context API that React 19 removes, so your component stops receiving context. Use `static contextType = MyContext` or `useContext()` in a function component, & update the provider too.";
35625
36427
  };
35626
- const isInsideClassBody = (node) => {
35627
- let current = node.parent;
35628
- while (current) {
35629
- if (isNodeOfType(current, "ClassBody")) return true;
35630
- if (isFunctionLike$2(current)) return false;
35631
- current = current.parent;
35632
- }
35633
- return false;
35634
- };
35635
36428
  const noLegacyContextApi = defineRule({
35636
36429
  id: "no-legacy-context-api",
35637
36430
  title: "Legacy context API",
@@ -35646,6 +36439,7 @@ const noLegacyContextApi = defineRule({
35646
36439
  if (!isNodeOfType(memberNode, "MethodDefinition") && !isNodeOfType(memberNode, "PropertyDefinition")) return;
35647
36440
  if (!isNodeOfType(memberNode.key, "Identifier")) return;
35648
36441
  if (!LEGACY_CONTEXT_NAMES.has(memberNode.key.name)) return;
36442
+ if (memberNode.key.name === "getChildContext" ? memberNode.static : !memberNode.static) return;
35649
36443
  context.report({
35650
36444
  node: memberNode.key,
35651
36445
  message: buildLegacyContextMessage(memberNode.key.name)
@@ -35653,6 +36447,8 @@ const noLegacyContextApi = defineRule({
35653
36447
  };
35654
36448
  return {
35655
36449
  ClassBody(node) {
36450
+ const classNode = node.parent;
36451
+ if (!classNode || !isProvenReactClassComponent(classNode, context.scopes)) return;
35656
36452
  for (const member of node.body ?? []) checkMember(member);
35657
36453
  },
35658
36454
  AssignmentExpression(node) {
@@ -35662,9 +36458,11 @@ const noLegacyContextApi = defineRule({
35662
36458
  if (left.computed) return;
35663
36459
  if (!isNodeOfType(left.property, "Identifier")) return;
35664
36460
  if (!LEGACY_CONTEXT_NAMES.has(left.property.name)) return;
35665
- if (!isNodeOfType(left.object, "Identifier")) return;
35666
- if (!isUppercaseName(left.object.name)) return;
35667
- if (isInsideClassBody(node)) return;
36461
+ if (left.property.name === "getChildContext") return;
36462
+ const component = stripParenExpression(left.object);
36463
+ if (!isNodeOfType(component, "Identifier")) return;
36464
+ const symbol = context.scopes.symbolFor(component);
36465
+ if (!symbol || !isProvenReactComponentSymbol(symbol, context.scopes, context.cfg, component) && (hasSymbolWriteBefore(symbol, component, context.scopes) || !symbolHasReactComponentTypeAnnotation(symbol, context.scopes))) return;
35668
36466
  context.report({
35669
36467
  node: left,
35670
36468
  message: buildLegacyContextMessage(left.property.name)
@@ -35868,10 +36666,10 @@ const getMethodCalls = (functionNode, scopes) => {
35868
36666
  const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, scopes, visitedSymbolIds, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
35869
36667
  if (visitedFunctionNodes.has(functionNode)) return false;
35870
36668
  visitedFunctionNodes.add(functionNode);
35871
- const usageFunction = findEnclosingFunction(usageNode);
36669
+ const usageFunction = findEnclosingFunction$1(usageNode);
35872
36670
  const immediateCall = getDirectCallForExpression(functionNode);
35873
36671
  if (immediateCall) {
35874
- const immediateCallFunction = findEnclosingFunction(immediateCall);
36672
+ const immediateCallFunction = findEnclosingFunction$1(immediateCall);
35875
36673
  if (immediateCallFunction === usageFunction) {
35876
36674
  const immediateCallStart = getRangeStart(immediateCall);
35877
36675
  return immediateCallStart === null || immediateCallStart < usageBoundary;
@@ -35880,7 +36678,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
35880
36678
  return isFunctionInvokedBeforeUsage(immediateCallFunction, usageNode, usageBoundary, scopes, visitedSymbolIds, new Set(visitedFunctionNodes));
35881
36679
  }
35882
36680
  for (const methodCall of getMethodCalls(functionNode, scopes)) {
35883
- const methodCallFunction = findEnclosingFunction(methodCall);
36681
+ const methodCallFunction = findEnclosingFunction$1(methodCall);
35884
36682
  if (methodCallFunction === usageFunction) {
35885
36683
  const methodCallStart = getRangeStart(methodCall);
35886
36684
  if (methodCallStart === null || methodCallStart < usageBoundary) return true;
@@ -35903,7 +36701,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
35903
36701
  if (scopes.symbolFor(child)?.declarationNode !== symbol.declarationNode) return;
35904
36702
  const call = getDirectCallForExpression(child);
35905
36703
  if (!call) return false;
35906
- const callFunction = findEnclosingFunction(call);
36704
+ const callFunction = findEnclosingFunction$1(call);
35907
36705
  if (callFunction === usageFunction) {
35908
36706
  const callStart = getRangeStart(call);
35909
36707
  wasInvokedBeforeUsage = callStart === null || callStart < usageBoundary;
@@ -35934,14 +36732,14 @@ const wasMutatedBeforeUsage = (symbol, usageNode, readNode, scopes, visitedMutat
35934
36732
  visitedMutationSymbolIds.add(symbol.id);
35935
36733
  const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, readNode) : inheritedUsageBoundary;
35936
36734
  if (typeof usageBoundary !== "number") return true;
35937
- const usageFunction = findEnclosingFunction(usageNode);
36735
+ const usageFunction = findEnclosingFunction$1(usageNode);
35938
36736
  return symbol.references.some((reference) => {
35939
36737
  const referenceStart = getRangeStart(reference.identifier);
35940
36738
  const simpleAlias = getSimpleAlias(reference.identifier, scopes);
35941
36739
  if (simpleAlias) return wasMutatedBeforeUsage(simpleAlias.symbol, usageNode, readNodesBySymbolId.get(simpleAlias.symbol.id) ?? simpleAlias.readNode, scopes, new Set(visitedMutationSymbolIds), usageBoundary, readNodesBySymbolId);
35942
36740
  if (!isPotentialMutationReference(reference.identifier, readNode)) return false;
35943
36741
  if (referenceStart === null) return true;
35944
- const mutationFunction = findEnclosingFunction(reference.identifier);
36742
+ const mutationFunction = findEnclosingFunction$1(reference.identifier);
35945
36743
  if (mutationFunction === usageFunction) return referenceStart < usageBoundary;
35946
36744
  if (!mutationFunction) return usageFunction !== null;
35947
36745
  if (usageFunction && isAstDescendant(usageFunction, mutationFunction)) return true;
@@ -36486,7 +37284,7 @@ const noMoment = defineRule({
36486
37284
  });
36487
37285
  //#endregion
36488
37286
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
36489
- const MESSAGE$18 = "This file declares several components, so each component is harder to find, test, and change.";
37287
+ const MESSAGE$19 = "This file declares several components, so each component is harder to find, test, and change.";
36490
37288
  const resolveSettings$16 = (settings) => {
36491
37289
  const reactDoctor = settings?.["react-doctor"];
36492
37290
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -36523,13 +37321,6 @@ const isReactNamespaceExpression = (node, scopes) => {
36523
37321
  return isReactNamespaceImport(node, scopes);
36524
37322
  };
36525
37323
  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));
36526
- const getDestructuredPropertyName$1 = (symbol) => {
36527
- const property = symbol.bindingIdentifier.parent;
36528
- if (!property || !isNodeOfType(property, "Property") || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
36529
- if (isNodeOfType(property.key, "Identifier") && !property.computed) return property.key.name;
36530
- if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
36531
- return null;
36532
- };
36533
37324
  const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
36534
37325
  if (visitedSymbolIds.has(symbol.id)) return false;
36535
37326
  visitedSymbolIds.add(symbol.id);
@@ -36539,7 +37330,7 @@ const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
36539
37330
  }
36540
37331
  const init = symbol.initializer;
36541
37332
  if (!init) return false;
36542
- const destructuredPropertyName = getDestructuredPropertyName$1(symbol);
37333
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
36543
37334
  if (destructuredPropertyName && REACT_HOC_NAMES.has(destructuredPropertyName) && isReactNamespaceExpression(init, scopes)) return true;
36544
37335
  if (isReactHocMemberReference(init, scopes)) return true;
36545
37336
  if (isNodeOfType(init, "Identifier")) {
@@ -36845,7 +37636,7 @@ const noMultiComp = defineRule({
36845
37636
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
36846
37637
  for (const component of flagged.slice(1)) context.report({
36847
37638
  node: component.reportNode,
36848
- message: MESSAGE$18
37639
+ message: MESSAGE$19
36849
37640
  });
36850
37641
  } };
36851
37642
  }
@@ -37058,7 +37849,7 @@ const resolveReducerFunction = (node, currentFilename) => {
37058
37849
  };
37059
37850
  //#endregion
37060
37851
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
37061
- const MESSAGE$17 = "This reducer changes state in place, so your update is silently skipped.";
37852
+ const MESSAGE$18 = "This reducer changes state in place, so your update is silently skipped.";
37062
37853
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
37063
37854
  "copyWithin",
37064
37855
  "fill",
@@ -37267,7 +38058,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
37267
38058
  reportedNodes.add(options.crossFileConsumerCallSite);
37268
38059
  context.report({
37269
38060
  node: options.crossFileConsumerCallSite,
37270
- message: `${MESSAGE$17} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
38061
+ message: `${MESSAGE$18} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
37271
38062
  });
37272
38063
  return;
37273
38064
  }
@@ -37276,7 +38067,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
37276
38067
  reportedNodes.add(mutation.node);
37277
38068
  context.report({
37278
38069
  node: mutation.node,
37279
- message: MESSAGE$17
38070
+ message: MESSAGE$18
37280
38071
  });
37281
38072
  }
37282
38073
  };
@@ -37683,7 +38474,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
37683
38474
  });
37684
38475
  //#endregion
37685
38476
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
37686
- const MESSAGE$16 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
38477
+ const MESSAGE$17 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
37687
38478
  const KEYBOARD_HANDLER_PROP_NAMES = [
37688
38479
  "onKeyDown",
37689
38480
  "onKeyUp",
@@ -37802,7 +38593,7 @@ const noNoninteractiveTabindex = defineRule({
37802
38593
  if (numeric === null) {
37803
38594
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
37804
38595
  node: tabIndex,
37805
- message: MESSAGE$16
38596
+ message: MESSAGE$17
37806
38597
  });
37807
38598
  return;
37808
38599
  }
@@ -37824,7 +38615,7 @@ const noNoninteractiveTabindex = defineRule({
37824
38615
  if (!roleAttribute) {
37825
38616
  context.report({
37826
38617
  node: tabIndex,
37827
- message: MESSAGE$16
38618
+ message: MESSAGE$17
37828
38619
  });
37829
38620
  return;
37830
38621
  }
@@ -37838,7 +38629,7 @@ const noNoninteractiveTabindex = defineRule({
37838
38629
  }
37839
38630
  context.report({
37840
38631
  node: tabIndex,
37841
- message: MESSAGE$16
38632
+ message: MESSAGE$17
37842
38633
  });
37843
38634
  } };
37844
38635
  }
@@ -38293,12 +39084,6 @@ const getDeclarationKind = (declarator) => {
38293
39084
  return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
38294
39085
  };
38295
39086
  const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
38296
- const getDestructuredPropertyName = (bindingIdentifier) => {
38297
- const property = bindingIdentifier.parent;
38298
- if (!property || !isNodeOfType(property, "Property")) return null;
38299
- if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
38300
- return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
38301
- };
38302
39087
  const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
38303
39088
  const unwrappedExpression = stripParenExpression(expression);
38304
39089
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -38308,7 +39093,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
38308
39093
  if (isProp(analysis, callbackReference)) {
38309
39094
  if (isWholePropsObjectReference(analysis, callbackReference)) return null;
38310
39095
  const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
38311
- return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
39096
+ return (bindingIdentifier && getDestructuredBindingPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
38312
39097
  }
38313
39098
  if (hasMutableBindingWrite(callbackReference)) return null;
38314
39099
  const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
@@ -38318,7 +39103,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
38318
39103
  visitedVariables.add(callbackVariable);
38319
39104
  if (isNodeOfType(declarator.id, "ObjectPattern")) {
38320
39105
  const bindingIdentifier = callbackVariable.defs[0]?.name;
38321
- const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
39106
+ const propertyName = bindingIdentifier ? getDestructuredBindingPropertyName(bindingIdentifier) : null;
38322
39107
  const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
38323
39108
  return propertyName && propsReference ? propertyName : null;
38324
39109
  }
@@ -38421,7 +39206,7 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
38421
39206
  const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
38422
39207
  if (!bindingProvenance) return null;
38423
39208
  const { refCall, variables } = bindingProvenance;
38424
- const componentFunction = findEnclosingFunction(effectCall);
39209
+ const componentFunction = findEnclosingFunction$1(effectCall);
38425
39210
  if (!componentFunction || !isFunctionLike$2(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
38426
39211
  if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
38427
39212
  const callbackPropNames = /* @__PURE__ */ new Set();
@@ -39257,174 +40042,6 @@ const noPropCallbackInRender = defineRule({
39257
40042
  } })
39258
40043
  });
39259
40044
  //#endregion
39260
- //#region src/plugin/utils/is-proven-react-class-component.ts
39261
- const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
39262
- const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
39263
- const expression = stripParenExpression(node);
39264
- if (isNodeOfType(expression, "MemberExpression")) {
39265
- const propertyName = getStaticPropertyName(expression);
39266
- const receiver = stripParenExpression(expression.object);
39267
- return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
39268
- }
39269
- if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
39270
- if (!isNodeOfType(expression, "Identifier")) return false;
39271
- const symbol = scopes.symbolFor(expression);
39272
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
39273
- visitedSymbolIds.add(symbol.id);
39274
- if (isImportedFromReact(symbol)) {
39275
- const importedName = getImportedName(symbol.declarationNode);
39276
- return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
39277
- }
39278
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
39279
- return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
39280
- };
39281
- const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39282
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
39283
- visitedClassNodes.add(classNode);
39284
- return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
39285
- };
39286
- //#endregion
39287
- //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
39288
- const functionContainsProvenReactHookCall = (functionNode, scopes) => {
39289
- if (!isFunctionLike$2(functionNode)) return false;
39290
- let containsReactHookCall = false;
39291
- walkAst(functionNode.body, (node) => {
39292
- if (containsReactHookCall) return false;
39293
- if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
39294
- if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
39295
- containsReactHookCall = true;
39296
- return false;
39297
- }
39298
- });
39299
- return containsReactHookCall;
39300
- };
39301
- //#endregion
39302
- //#region src/plugin/utils/function-returns-props-children.ts
39303
- const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
39304
- if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
39305
- const firstParameter = stripParenExpression(functionNode.params[0]);
39306
- const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
39307
- const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
39308
- const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
39309
- if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
39310
- for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
39311
- const propertyValue = stripParenExpression(property.value);
39312
- const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
39313
- if (!isNodeOfType(childrenBinding, "Identifier")) continue;
39314
- const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
39315
- if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
39316
- }
39317
- }
39318
- return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
39319
- const candidate = stripParenExpression(expression);
39320
- if (isNodeOfType(candidate, "Identifier")) {
39321
- const symbol = scopes.symbolFor(candidate);
39322
- return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
39323
- }
39324
- if (!isNodeOfType(candidate, "MemberExpression")) return false;
39325
- if (getStaticPropertyName(candidate) !== "children") return false;
39326
- const receiver = stripParenExpression(candidate.object);
39327
- if (!isNodeOfType(receiver, "Identifier")) return false;
39328
- const receiverSymbol = scopes.symbolFor(receiver);
39329
- if (!receiverSymbol || !propsParameterSymbol) return false;
39330
- return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
39331
- }, controlFlow);
39332
- };
39333
- //#endregion
39334
- //#region src/plugin/utils/function-returns-only-null.ts
39335
- const isNullExpression = (expression) => {
39336
- const candidate = stripParenExpression(expression);
39337
- return isNodeOfType(candidate, "Literal") && candidate.value === null;
39338
- };
39339
- const functionReturnsOnlyNull = (functionNode) => {
39340
- if (!isFunctionLike$2(functionNode)) return false;
39341
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
39342
- const returnStatements = collectFunctionReturnStatements(functionNode);
39343
- return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
39344
- };
39345
- //#endregion
39346
- //#region src/plugin/utils/is-proven-styled-component-expression.ts
39347
- const findFactoryRoot = (node) => {
39348
- const candidate = stripParenExpression(node);
39349
- if (isNodeOfType(candidate, "Identifier")) return candidate;
39350
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
39351
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
39352
- return null;
39353
- };
39354
- const findFactoryPropertyName = (node) => {
39355
- const candidate = stripParenExpression(node);
39356
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
39357
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
39358
- return null;
39359
- };
39360
- const isProvenStyledComponentExpression = (expression, scopes) => {
39361
- const candidate = stripParenExpression(expression);
39362
- if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
39363
- const factoryRoot = findFactoryRoot(candidate.tag);
39364
- if (!factoryRoot) return false;
39365
- const factoryPropertyName = findFactoryPropertyName(candidate.tag);
39366
- if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
39367
- const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
39368
- if (!symbol || symbol.kind !== "import") return false;
39369
- if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
39370
- const importDeclaration = symbol.declarationNode.parent;
39371
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
39372
- };
39373
- //#endregion
39374
- //#region src/plugin/utils/is-proven-react-component-symbol.ts
39375
- const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
39376
- const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39377
- const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39378
- const candidate = stripParenExpression(expression);
39379
- if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
39380
- if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
39381
- if (isProvenStyledComponentExpression(candidate, scopes)) return true;
39382
- if (isNodeOfType(candidate, "Identifier")) {
39383
- const symbol = scopes.symbolFor(candidate);
39384
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
39385
- visitedSymbolIds.add(symbol.id);
39386
- if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
39387
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
39388
- return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
39389
- }
39390
- if (!isNodeOfType(candidate, "CallExpression")) return false;
39391
- if (!hasStableCallTarget(candidate, scopes)) return false;
39392
- if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
39393
- const wrappedComponent = candidate.arguments[0];
39394
- return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
39395
- }
39396
- if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
39397
- const factory = candidate.arguments[0];
39398
- if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
39399
- const unwrappedFactory = stripParenExpression(factory);
39400
- if (!isInlineFunctionExpression(unwrappedFactory)) return false;
39401
- if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
39402
- const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
39403
- const returnedExpression = returnStatements[0]?.argument;
39404
- return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
39405
- };
39406
- const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
39407
- const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
39408
- for (const candidateSymbol of candidateSymbols) {
39409
- if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
39410
- if (isComponentDeclaration(candidateSymbol.declarationNode)) {
39411
- if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
39412
- continue;
39413
- }
39414
- const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
39415
- if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
39416
- if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
39417
- continue;
39418
- }
39419
- if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
39420
- if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
39421
- continue;
39422
- }
39423
- if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
39424
- }
39425
- return false;
39426
- };
39427
- //#endregion
39428
40045
  //#region src/plugin/rules/architecture/no-prop-types.ts
39429
40046
  const PROP_TYPES_PROPERTY = "propTypes";
39430
40047
  const isPropTypesKey = (key, computed) => {
@@ -39577,7 +40194,7 @@ const isModuleScopedBinding = (identifier) => {
39577
40194
  const binding = findVariableInitializer(identifier, identifier.name);
39578
40195
  if (!binding) return false;
39579
40196
  if (isNodeOfType(binding.scopeOwner, "Program")) return true;
39580
- if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction(binding.scopeOwner) === null;
40197
+ if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction$1(binding.scopeOwner) === null;
39581
40198
  return false;
39582
40199
  };
39583
40200
  const looksLikeFreshUpdateExpression = (expression) => {
@@ -39632,7 +40249,7 @@ const noRandomKey = defineRule({
39632
40249
  });
39633
40250
  //#endregion
39634
40251
  //#region src/plugin/rules/react-builtins/no-react-children.ts
39635
- const MESSAGE$15 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
40252
+ const MESSAGE$16 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
39636
40253
  const isChildrenIdentifier = (node, contextNode) => {
39637
40254
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
39638
40255
  return isImportedFromModule(contextNode, "Children", "react");
@@ -39658,13 +40275,13 @@ const noReactChildren = defineRule({
39658
40275
  if (isChildrenIdentifier(memberObject, node)) {
39659
40276
  context.report({
39660
40277
  node: calleeOuter,
39661
- message: MESSAGE$15
40278
+ message: MESSAGE$16
39662
40279
  });
39663
40280
  return;
39664
40281
  }
39665
40282
  if (isReactNamespaceMember(memberObject, node)) context.report({
39666
40283
  node: calleeOuter,
39667
- message: MESSAGE$15
40284
+ message: MESSAGE$16
39668
40285
  });
39669
40286
  } })
39670
40287
  });
@@ -40345,7 +40962,7 @@ const noRenderPropChildren = defineRule({
40345
40962
  });
40346
40963
  //#endregion
40347
40964
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
40348
- const MESSAGE$14 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40965
+ const MESSAGE$15 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40349
40966
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
40350
40967
  const isUsedAsReturnValue = (parent) => {
40351
40968
  if (!parent) return false;
@@ -40363,7 +40980,7 @@ const noRenderReturnValue = defineRule({
40363
40980
  if (!isUsedAsReturnValue(node.parent)) return;
40364
40981
  context.report({
40365
40982
  node: node.callee,
40366
- message: MESSAGE$14
40983
+ message: MESSAGE$15
40367
40984
  });
40368
40985
  } })
40369
40986
  });
@@ -41172,7 +41789,7 @@ const noSelfUpdatingEffect = defineRule({
41172
41789
  });
41173
41790
  //#endregion
41174
41791
  //#region src/plugin/rules/react-builtins/no-set-state.ts
41175
- const MESSAGE$13 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41792
+ const MESSAGE$14 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41176
41793
  const noSetState = defineRule({
41177
41794
  id: "no-set-state",
41178
41795
  title: "Local class state forbidden",
@@ -41187,7 +41804,7 @@ const noSetState = defineRule({
41187
41804
  if (!getParentComponent(node)) return;
41188
41805
  context.report({
41189
41806
  node: node.callee,
41190
- message: MESSAGE$13
41807
+ message: MESSAGE$14
41191
41808
  });
41192
41809
  } })
41193
41810
  });
@@ -41494,9 +42111,9 @@ const isReturnedFromAnyEffectInScope = (functionNode, ownerScope) => {
41494
42111
  return isReturnedFromEffect;
41495
42112
  };
41496
42113
  const isInsideEffectCleanupReturn = (node, ownerScope) => {
41497
- let functionNode = findEnclosingFunction(node);
42114
+ let functionNode = findEnclosingFunction$1(node);
41498
42115
  while (functionNode) {
41499
- const outerFunction = findEnclosingFunction(functionNode);
42116
+ const outerFunction = findEnclosingFunction$1(functionNode);
41500
42117
  if (outerFunction && isEffectCallbackFunction(outerFunction) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
41501
42118
  if (isReturnedFromAnyEffectInScope(functionNode, ownerScope)) return true;
41502
42119
  functionNode = outerFunction;
@@ -41504,7 +42121,7 @@ const isInsideEffectCleanupReturn = (node, ownerScope) => {
41504
42121
  return false;
41505
42122
  };
41506
42123
  const hasRefCurrentReassignmentAfterClear = (clearCall, refName) => {
41507
- const enclosingFunction = findEnclosingFunction(clearCall);
42124
+ const enclosingFunction = findEnclosingFunction$1(clearCall);
41508
42125
  if (!isFunctionLike$2(enclosingFunction)) return false;
41509
42126
  if (!isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
41510
42127
  const clearStart = getRangeStart(clearCall);
@@ -41557,7 +42174,7 @@ const isAbstractRole = (openingElement, settings) => {
41557
42174
  };
41558
42175
  //#endregion
41559
42176
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
41560
- const MESSAGE$12 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
42177
+ const MESSAGE$13 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
41561
42178
  const DEFAULT_HANDLERS = [
41562
42179
  "onClick",
41563
42180
  "onMouseDown",
@@ -41672,7 +42289,7 @@ const noStaticElementInteractions = defineRule({
41672
42289
  if (!roleAttribute || !roleAttribute.value) {
41673
42290
  context.report({
41674
42291
  node: node.name,
41675
- message: MESSAGE$12
42292
+ message: MESSAGE$13
41676
42293
  });
41677
42294
  return;
41678
42295
  }
@@ -41682,7 +42299,7 @@ const noStaticElementInteractions = defineRule({
41682
42299
  if (isRecognizedRoleString(attributeValue.value)) return;
41683
42300
  context.report({
41684
42301
  node: node.name,
41685
- message: MESSAGE$12
42302
+ message: MESSAGE$13
41686
42303
  });
41687
42304
  return;
41688
42305
  }
@@ -41690,7 +42307,7 @@ const noStaticElementInteractions = defineRule({
41690
42307
  if (!settings.allowExpressionValues) {
41691
42308
  context.report({
41692
42309
  node: node.name,
41693
- message: MESSAGE$12
42310
+ message: MESSAGE$13
41694
42311
  });
41695
42312
  return;
41696
42313
  }
@@ -41698,7 +42315,7 @@ const noStaticElementInteractions = defineRule({
41698
42315
  if (isStaticNullishExpression(expression)) {
41699
42316
  context.report({
41700
42317
  node: node.name,
41701
- message: MESSAGE$12
42318
+ message: MESSAGE$13
41702
42319
  });
41703
42320
  return;
41704
42321
  }
@@ -41708,7 +42325,7 @@ const noStaticElementInteractions = defineRule({
41708
42325
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
41709
42326
  context.report({
41710
42327
  node: node.name,
41711
- message: MESSAGE$12
42328
+ message: MESSAGE$13
41712
42329
  });
41713
42330
  return;
41714
42331
  }
@@ -41716,7 +42333,7 @@ const noStaticElementInteractions = defineRule({
41716
42333
  }
41717
42334
  context.report({
41718
42335
  node: node.name,
41719
- message: MESSAGE$12
42336
+ message: MESSAGE$13
41720
42337
  });
41721
42338
  } };
41722
42339
  }
@@ -41822,7 +42439,7 @@ const noStringRefs = defineRule({
41822
42439
  });
41823
42440
  //#endregion
41824
42441
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
41825
- const MESSAGE$11 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
42442
+ const MESSAGE$12 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
41826
42443
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
41827
42444
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
41828
42445
  const noSyncXhr = defineRule({
@@ -41843,7 +42460,7 @@ const noSyncXhr = defineRule({
41843
42460
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
41844
42461
  context.report({
41845
42462
  node,
41846
- message: MESSAGE$11
42463
+ message: MESSAGE$12
41847
42464
  });
41848
42465
  };
41849
42466
  return {
@@ -41868,7 +42485,7 @@ const noSyncXhr = defineRule({
41868
42485
  });
41869
42486
  //#endregion
41870
42487
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
41871
- const MESSAGE$10 = "This value is `undefined` because function components have no `this`.";
42488
+ const MESSAGE$11 = "This value is `undefined` because function components have no `this`.";
41872
42489
  const isInsideClassMethod = (node, customClassFactoryNames) => {
41873
42490
  let ancestor = node.parent;
41874
42491
  while (ancestor) {
@@ -41956,7 +42573,7 @@ const noThisInSfc = defineRule({
41956
42573
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
41957
42574
  context.report({
41958
42575
  node,
41959
- message: MESSAGE$10
42576
+ message: MESSAGE$11
41960
42577
  });
41961
42578
  } };
41962
42579
  }
@@ -42334,7 +42951,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
42334
42951
  return false;
42335
42952
  };
42336
42953
  const isAfterAvailabilityEarlyExit = (node, componentOrHookNode, browserGlobalName, context) => {
42337
- const enclosingFunction = findEnclosingFunction(node);
42954
+ const enclosingFunction = findEnclosingFunction$1(node);
42338
42955
  if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$2(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
42339
42956
  let currentNode = node;
42340
42957
  while (currentNode !== enclosingFunction) {
@@ -43601,7 +44218,11 @@ const resolveSettings$8 = (settings) => {
43601
44218
  propNamePattern: ruleSettings.propNamePattern ?? "render*"
43602
44219
  };
43603
44220
  };
43604
- const expressionContainsJsxOrCreateElement = (root) => {
44221
+ const isReactCreateElementCall = (node, scopes) => isReactApiCall(node, "createElement", scopes, {
44222
+ allowGlobalReactNamespace: true,
44223
+ resolveNamedAliases: true
44224
+ });
44225
+ const expressionContainsJsxOrCreateElement = (root, scopes) => {
43605
44226
  let found = false;
43606
44227
  walkAst(root, (node) => {
43607
44228
  if (found) return false;
@@ -43610,17 +44231,17 @@ const expressionContainsJsxOrCreateElement = (root) => {
43610
44231
  found = true;
43611
44232
  return false;
43612
44233
  }
43613
- if (isNodeOfType(node, "CallExpression") && isCreateElementCall(node)) {
44234
+ if (isNodeOfType(node, "CallExpression") && isReactCreateElementCall(node, scopes)) {
43614
44235
  found = true;
43615
44236
  return false;
43616
44237
  }
43617
44238
  });
43618
44239
  return found;
43619
44240
  };
43620
- const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement, controlFlow);
43621
- const isReactClassComponent = (classNode) => {
44241
+ const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => expressionContainsJsxOrCreateElement(expression, scopes), controlFlow);
44242
+ const isReactClassComponent = (classNode, scopes) => {
43622
44243
  if (isEs6Component(classNode)) return true;
43623
- return expressionContainsJsxOrCreateElement(classNode);
44244
+ return expressionContainsJsxOrCreateElement(classNode, scopes);
43624
44245
  };
43625
44246
  const findEnclosingComponent = (node, scopes, controlFlow) => {
43626
44247
  let walker = node.parent;
@@ -43637,7 +44258,7 @@ const findEnclosingComponent = (node, scopes, controlFlow) => {
43637
44258
  };
43638
44259
  }
43639
44260
  if (isNodeOfType(walker, "ClassDeclaration") || isNodeOfType(walker, "ClassExpression")) {
43640
- if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker)) return {
44261
+ if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker, scopes)) return {
43641
44262
  component: walker,
43642
44263
  name: walker.id.name
43643
44264
  };
@@ -43726,12 +44347,12 @@ const isObjectCallbackCandidate = (node) => {
43726
44347
  }
43727
44348
  return false;
43728
44349
  };
43729
- const hocCallContainsComponent = (call) => {
44350
+ const hocCallContainsComponent = (call, scopes) => {
43730
44351
  const firstArgument = call.arguments[0];
43731
44352
  if (!firstArgument) return false;
43732
- if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument);
43733
- if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument);
43734
- return expressionContainsJsxOrCreateElement(firstArgument);
44353
+ if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument, scopes);
44354
+ if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument, scopes);
44355
+ return expressionContainsJsxOrCreateElement(firstArgument, scopes);
43735
44356
  };
43736
44357
  const isFirstArgumentOfHocCall = (node) => {
43737
44358
  const parent = node.parent;
@@ -43764,19 +44385,35 @@ const isReturnOfMapCallback = (node) => {
43764
44385
  }
43765
44386
  return false;
43766
44387
  };
43767
- const isCloneElementCall = (node) => {
43768
- if (!isNodeOfType(node, "CallExpression")) return false;
43769
- const callee = node.callee;
43770
- if (isNodeOfType(callee, "Identifier")) return callee.name === "cloneElement";
43771
- return isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "cloneElement";
43772
- };
43773
44388
  const TS_VALUE_PASSTHROUGH_TYPES = new Set([
43774
44389
  "TSAsExpression",
43775
44390
  "TSNonNullExpression",
43776
44391
  "TSSatisfiesExpression",
43777
44392
  "TSTypeAssertion"
43778
44393
  ]);
43779
- const isRenderFlowingReadReference = (identifier) => {
44394
+ const ELEMENT_TYPE_PROP_NAMES = new Set([
44395
+ "as",
44396
+ "body",
44397
+ "calendarcontainer",
44398
+ "component",
44399
+ "fallback",
44400
+ "tooltip"
44401
+ ]);
44402
+ const isElementTypeJsxAttribute = (node) => {
44403
+ if (!isNodeOfType(node, "JSXAttribute")) return false;
44404
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return false;
44405
+ const attributeName = node.name.name;
44406
+ return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
44407
+ };
44408
+ const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
44409
+ allowGlobalReactNamespace: true,
44410
+ resolveNamedAliases: true
44411
+ });
44412
+ const isReactLazyCall = (call, scopes) => isReactApiCall(call, "lazy", scopes, {
44413
+ allowGlobalReactNamespace: true,
44414
+ resolveNamedAliases: true
44415
+ });
44416
+ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
43780
44417
  let valueNode = identifier;
43781
44418
  let parent = valueNode.parent;
43782
44419
  while (parent) {
@@ -43786,20 +44423,26 @@ const isRenderFlowingReadReference = (identifier) => {
43786
44423
  continue;
43787
44424
  }
43788
44425
  switch (parent.type) {
43789
- case "JSXExpressionContainer":
43790
- case "ReturnStatement": return true;
43791
- case "ArrowFunctionExpression": return parent.body === valueNode;
44426
+ case "JSXOpeningElement": return parent.name === valueNode;
44427
+ case "JSXExpressionContainer": return Boolean(parent.parent && isElementTypeJsxAttribute(parent.parent));
44428
+ case "ReturnStatement": return false;
44429
+ case "ArrowFunctionExpression": return false;
43792
44430
  case "CallExpression":
43793
44431
  if (parent.callee === valueNode) return false;
43794
- if (isCreateElementCall(parent) || isCloneElementCall(parent)) return true;
44432
+ if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
44433
+ if (isReactCreateElementCall(parent, scopes)) return true;
43795
44434
  valueNode = parent;
43796
44435
  parent = parent.parent;
43797
44436
  continue;
43798
- case "VariableDeclarator": return parent.init === valueNode && isNodeOfType(parent.id, "Identifier") && isReactComponentName(parent.id.name);
43799
- case "AssignmentExpression": {
43800
- const assignmentTarget = parent.left;
43801
- return parent.right === valueNode && isNodeOfType(assignmentTarget, "Identifier") && isReactComponentName(assignmentTarget.name);
44437
+ case "VariableDeclarator": {
44438
+ if (parent.init !== valueNode || !isNodeOfType(parent.id, "Identifier")) return false;
44439
+ const aliasSymbol = scopes.symbolFor(parent.id);
44440
+ if (!aliasSymbol || aliasSymbol.kind !== "const" || visitedSymbols.has(aliasSymbol.id)) return false;
44441
+ const nextVisitedSymbols = new Set(visitedSymbols);
44442
+ nextVisitedSymbols.add(aliasSymbol.id);
44443
+ return aliasSymbol.references.some((reference) => reference.flag === "read" && isRenderFlowingReadReference(reference.identifier, scopes, nextVisitedSymbols));
43802
44444
  }
44445
+ case "AssignmentExpression": return false;
43803
44446
  case "Property":
43804
44447
  if (parent.value !== valueNode) return false;
43805
44448
  valueNode = parent;
@@ -43837,6 +44480,7 @@ const noUnstableNestedComponents = defineRule({
43837
44480
  severity: "warn",
43838
44481
  recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
43839
44482
  category: "Performance",
44483
+ tags: ["react-jsx-only"],
43840
44484
  create: (context) => {
43841
44485
  const settings = resolveSettings$8(context.settings);
43842
44486
  const renderPropRegex = compileGlob(settings.propNamePattern);
@@ -43899,7 +44543,7 @@ const noUnstableNestedComponents = defineRule({
43899
44543
  },
43900
44544
  Identifier(node) {
43901
44545
  if (!isReactComponentName(node.name)) return;
43902
- if (!isRenderFlowingReadReference(node)) return;
44546
+ if (!isRenderFlowingReadReference(node, context.scopes)) return;
43903
44547
  recordInstantiation(node, node.name);
43904
44548
  },
43905
44549
  FunctionDeclaration: checkFunctionLike,
@@ -43908,28 +44552,33 @@ const noUnstableNestedComponents = defineRule({
43908
44552
  ClassDeclaration(node) {
43909
44553
  if (!node.id) return;
43910
44554
  if (!isReactComponentName(node.id.name)) return;
43911
- if (!isReactClassComponent(node)) return;
44555
+ if (!isReactClassComponent(node, context.scopes)) return;
43912
44556
  enqueueCandidate(node, null);
43913
44557
  },
43914
44558
  ClassExpression(node) {
43915
44559
  const inferredName = node.id?.name ?? inferFunctionLikeName(node);
43916
44560
  if (!inferredName || !isReactComponentName(inferredName)) return;
43917
- if (!isReactClassComponent(node)) return;
44561
+ if (!isReactClassComponent(node, context.scopes)) return;
43918
44562
  enqueueCandidate(node, null);
43919
44563
  },
43920
44564
  CallExpression(node) {
43921
- if (isCreateElementCall(node)) {
44565
+ if (isReactCreateElementCall(node, context.scopes)) {
43922
44566
  const firstArgument = node.arguments[0];
43923
44567
  if (firstArgument && isNodeOfType(firstArgument, "Identifier")) recordInstantiation(firstArgument, firstArgument.name);
43924
44568
  else if (firstArgument && isNodeOfType(firstArgument, "MemberExpression")) recordMemberChainInstantiation(firstArgument);
43925
44569
  }
43926
- if (!isHocCallee$1(node)) return;
43927
- if (!hocCallContainsComponent(node)) return;
43928
- enqueueCandidate(node, null);
44570
+ const isReactLazy = isReactLazyCall(node, context.scopes);
44571
+ if (!isReactLazy && !isHocCallee$1(node)) return;
44572
+ if (!isReactLazy && !hocCallContainsComponent(node, context.scopes)) return;
44573
+ const inferredName = inferFunctionLikeName(node);
44574
+ const propInfo = isComponentDeclaredInProp(node);
44575
+ if (propInfo === null && (!inferredName || !isReactComponentName(inferredName))) return;
44576
+ enqueueCandidate(node, propInfo === null ? inferredName : null);
43929
44577
  },
43930
44578
  "Program:exit"() {
43931
44579
  for (const report of queuedReports) {
43932
44580
  if (report.requiredInstantiationName !== null) {
44581
+ if ((report.requiredInstantiationBinding ? context.scopes.symbolFor(report.requiredInstantiationBinding) : null)?.references.some((reference) => reference.flag === "write" || reference.flag === "read-write")) continue;
43933
44582
  if (!(report.requiredInstantiationBinding !== null ? instantiatedBindingIdentifiers.has(report.requiredInstantiationBinding) : instantiatedComponentNames.has(report.requiredInstantiationName))) continue;
43934
44583
  }
43935
44584
  context.report({
@@ -44103,7 +44752,7 @@ const noWideLetterSpacing = defineRule({
44103
44752
  //#endregion
44104
44753
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
44105
44754
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
44106
- const MESSAGE$9 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
44755
+ const MESSAGE$10 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
44107
44756
  const resolveSettings$7 = (settings) => {
44108
44757
  const reactDoctor = settings?.["react-doctor"];
44109
44758
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -44137,7 +44786,7 @@ const noWillUpdateSetState = defineRule({
44137
44786
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
44138
44787
  context.report({
44139
44788
  node: node.callee,
44140
- message: MESSAGE$9
44789
+ message: MESSAGE$10
44141
44790
  });
44142
44791
  } };
44143
44792
  }
@@ -44962,7 +45611,7 @@ const findChildrenDestructuringFunction = (identifier, scopes) => {
44962
45611
  const symbol = scopes.symbolFor(identifier);
44963
45612
  if (symbol) {
44964
45613
  if (symbol.kind !== "parameter") return null;
44965
- const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45614
+ const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
44966
45615
  if (declaringFunction && destructuresChildrenAsFirstParam(declaringFunction)) return declaringFunction;
44967
45616
  return null;
44968
45617
  }
@@ -45008,7 +45657,7 @@ const isChildrenMemberExpression = (node, scopes) => {
45008
45657
  const symbol = scopes.symbolFor(propsObject);
45009
45658
  if (symbol) {
45010
45659
  if (symbol.kind !== "parameter") return false;
45011
- const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45660
+ const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
45012
45661
  return declaringFunction ? isDeclaringFunctionComponentLike(declaringFunction) : false;
45013
45662
  }
45014
45663
  return hasComponentLikeAncestorFunction(node);
@@ -45133,7 +45782,7 @@ const preactNoRenderArguments = defineRule({
45133
45782
  });
45134
45783
  //#endregion
45135
45784
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
45136
- const MESSAGE$8 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
45785
+ const MESSAGE$9 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
45137
45786
  const preactPreferOndblclick = defineRule({
45138
45787
  id: "preact-prefer-ondblclick",
45139
45788
  title: "onDoubleClick instead of onDblClick",
@@ -45148,7 +45797,7 @@ const preactPreferOndblclick = defineRule({
45148
45797
  if (!onDoubleClickAttribute) return;
45149
45798
  context.report({
45150
45799
  node: onDoubleClickAttribute,
45151
- message: MESSAGE$8
45800
+ message: MESSAGE$9
45152
45801
  });
45153
45802
  } })
45154
45803
  });
@@ -45399,7 +46048,7 @@ const preferExplicitVariants = defineRule({
45399
46048
  });
45400
46049
  //#endregion
45401
46050
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
45402
- const MESSAGE$7 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
46051
+ const MESSAGE$8 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
45403
46052
  const resolveSettings$4 = (settings) => {
45404
46053
  const reactDoctor = settings?.["react-doctor"];
45405
46054
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -45438,7 +46087,7 @@ const preferFunctionComponent = defineRule({
45438
46087
  const reportNode = node.id ?? node;
45439
46088
  context.report({
45440
46089
  node: reportNode,
45441
- message: MESSAGE$7
46090
+ message: MESSAGE$8
45442
46091
  });
45443
46092
  };
45444
46093
  return {
@@ -45660,7 +46309,7 @@ const doesFunctionReturnsObjectLiteral = (functionNode) => {
45660
46309
  //#endregion
45661
46310
  //#region src/plugin/utils/enclosing-component-or-hook-scope.ts
45662
46311
  const enclosingComponentOrHookScope = (startNode, ownScopeFor) => {
45663
- const functionNode = findEnclosingFunction(startNode);
46312
+ const functionNode = findEnclosingFunction$1(startNode);
45664
46313
  if (!functionNode) return null;
45665
46314
  const displayName = componentOrHookDisplayNameForFunction(functionNode);
45666
46315
  if (!displayName) return null;
@@ -46722,7 +47371,7 @@ const isTanstackQuerySource = (source) => TANSTACK_QUERY_PACKAGE_PATTERN.test(so
46722
47371
  //#region src/plugin/rules/tanstack-query/query-destructure-result.ts
46723
47372
  const isHookReturnForwardingSpread = (objectExpression) => {
46724
47373
  const objectParent = objectExpression.parent;
46725
- const enclosingFunction = findEnclosingFunction(objectExpression);
47374
+ const enclosingFunction = findEnclosingFunction$1(objectExpression);
46726
47375
  if (!enclosingFunction) return false;
46727
47376
  if (!(isNodeOfType(objectParent, "ReturnStatement") || isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && enclosingFunction.body === objectExpression)) return false;
46728
47377
  const enclosingName = componentOrHookDisplayNameForFunction(enclosingFunction);
@@ -47100,10 +47749,10 @@ const hasSuspensionBefore = (functionNode, boundary, context) => {
47100
47749
  return hasSuspension;
47101
47750
  };
47102
47751
  const isFunctionAncestor = (ancestor, functionNode) => {
47103
- let enclosingFunction = findEnclosingFunction(functionNode);
47752
+ let enclosingFunction = findEnclosingFunction$1(functionNode);
47104
47753
  while (enclosingFunction) {
47105
47754
  if (enclosingFunction === ancestor) return true;
47106
- enclosingFunction = findEnclosingFunction(enclosingFunction);
47755
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47107
47756
  }
47108
47757
  return false;
47109
47758
  };
@@ -47125,7 +47774,7 @@ const functionInvokesTarget = (callerFunction, targetFunction, context, visitedF
47125
47774
  return invokesTarget;
47126
47775
  };
47127
47776
  const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
47128
- const boundaryFunction = findEnclosingFunction(boundary);
47777
+ const boundaryFunction = findEnclosingFunction$1(boundary);
47129
47778
  const boundaryStart = getRangeStart(boundary);
47130
47779
  if (!boundaryFunction || boundaryStart === null) return false;
47131
47780
  let isInvokedBefore = false;
@@ -47162,11 +47811,11 @@ const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, conte
47162
47811
  const isWriteExecutedBefore = (writeNode, boundary, context, deferredExecutionFunction) => {
47163
47812
  const writeStart = getRangeStart(writeNode);
47164
47813
  const boundaryStart = getRangeStart(boundary);
47165
- const writeFunction = findEnclosingFunction(writeNode);
47814
+ const writeFunction = findEnclosingFunction$1(writeNode);
47166
47815
  const writeExecutionBoundary = writeFunction ?? findProgramRoot(writeNode);
47167
47816
  if (writeStart === null || boundaryStart === null || !writeExecutionBoundary || !isNodeReachableWithinFunction(writeNode, context) || !isUnconditionallyExecuted(writeNode, writeExecutionBoundary, context)) return false;
47168
- const boundaryFunction = findEnclosingFunction(boundary);
47169
- const renderFunction = deferredExecutionFunction ? findEnclosingFunction(deferredExecutionFunction) : null;
47817
+ const boundaryFunction = findEnclosingFunction$1(boundary);
47818
+ const renderFunction = deferredExecutionFunction ? findEnclosingFunction$1(deferredExecutionFunction) : null;
47170
47819
  if (writeFunction === boundaryFunction) return writeStart < boundaryStart;
47171
47820
  if (!writeFunction) return writeStart < boundaryStart;
47172
47821
  if (boundaryFunction && isFunctionAncestor(writeFunction, boundaryFunction)) {
@@ -47438,8 +48087,8 @@ const queryStableQueryClient = defineRule({
47438
48087
  create: (context) => ({ NewExpression(node) {
47439
48088
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "QueryClient") return;
47440
48089
  if (isStableHookWrapperArgument(node)) return;
47441
- let enclosingFunction = findEnclosingFunction(node);
47442
- while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
48090
+ let enclosingFunction = findEnclosingFunction$1(node);
48091
+ while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47443
48092
  if (!enclosingFunction) return;
47444
48093
  if (!isComponentFunction(enclosingFunction)) return;
47445
48094
  context.report({
@@ -47534,7 +48183,7 @@ const reactCompilerNoManualMemoization = defineRule({
47534
48183
  const comparatorArgument = node.arguments?.[1];
47535
48184
  if (comparatorArgument && !isNullishComparatorArgument(comparatorArgument)) return;
47536
48185
  } else {
47537
- const enclosingFunction = findEnclosingFunction(node);
48186
+ const enclosingFunction = findEnclosingFunction$1(node);
47538
48187
  if (!enclosingFunction || !isCompilerInferableFunction(enclosingFunction)) return;
47539
48188
  }
47540
48189
  const removalMessage = REMOVAL_MESSAGE_BY_REACT_API_NAME.get(apiName);
@@ -47547,7 +48196,7 @@ const reactCompilerNoManualMemoization = defineRule({
47547
48196
  });
47548
48197
  //#endregion
47549
48198
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
47550
- const MESSAGE$6 = "This JSX crashes because `React` isn't in scope.";
48199
+ const MESSAGE$7 = "This JSX crashes because `React` isn't in scope.";
47551
48200
  const reactInJsxScope = defineRule({
47552
48201
  id: "react-in-jsx-scope",
47553
48202
  title: "React not in scope for JSX",
@@ -47559,19 +48208,190 @@ const reactInJsxScope = defineRule({
47559
48208
  if (findVariableInitializer(node, "React")) return;
47560
48209
  context.report({
47561
48210
  node: node.name,
47562
- message: MESSAGE$6
48211
+ message: MESSAGE$7
47563
48212
  });
47564
48213
  },
47565
48214
  JSXFragment(node) {
47566
48215
  if (findVariableInitializer(node, "React")) return;
47567
48216
  context.report({
47568
48217
  node: node.openingFragment,
47569
- message: MESSAGE$6
48218
+ message: MESSAGE$7
47570
48219
  });
47571
48220
  }
47572
48221
  })
47573
48222
  });
47574
48223
  //#endregion
48224
+ //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
48225
+ const MESSAGE$6 = "React Markdown parses dynamic raw HTML when `rehype-raw` is enabled. Add `rehype-sanitize` to `rehypePlugins` or sanitize the markdown before rendering it.";
48226
+ const REACT_MARKDOWN_MODULE = "react-markdown";
48227
+ const REHYPE_RAW_MODULE = "rehype-raw";
48228
+ const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
48229
+ const DOMPURIFY_MODULES = new Set(["dompurify", "isomorphic-dompurify"]);
48230
+ const REACT_MARKDOWN_NAMED_EXPORTS = new Set(["MarkdownAsync", "MarkdownHooks"]);
48231
+ const REACT_MARKDOWN_NAMESPACE_EXPORTS = new Set(["default", ...REACT_MARKDOWN_NAMED_EXPORTS]);
48232
+ const DEFAULT_EXPORT_NAMES = new Set(["default"]);
48233
+ const getImportDeclaration = (symbol) => {
48234
+ if (symbol.kind !== "import") return null;
48235
+ const importDeclaration = symbol.declarationNode.parent;
48236
+ return isNodeOfType(importDeclaration, "ImportDeclaration") ? importDeclaration : null;
48237
+ };
48238
+ const isImportFromModule = (symbol, moduleName) => getImportDeclaration(symbol)?.source.value === moduleName;
48239
+ const isDefaultImportSymbol = (symbol, moduleName) => {
48240
+ if (!isImportFromModule(symbol, moduleName)) return false;
48241
+ return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "default";
48242
+ };
48243
+ const resolveImportedIdentifier = (node, scopes) => {
48244
+ if (!isNodeOfType(node, "Identifier") && !isNodeOfType(node, "JSXIdentifier")) return null;
48245
+ const symbol = resolveConstIdentifierAlias(node, scopes);
48246
+ return symbol?.kind === "import" ? symbol : null;
48247
+ };
48248
+ const getStaticMemberParts = (node) => {
48249
+ if (isNodeOfType(node, "MemberExpression")) {
48250
+ if (node.computed || !isNodeOfType(node.property, "Identifier")) return null;
48251
+ return {
48252
+ object: node.object,
48253
+ propertyName: node.property.name
48254
+ };
48255
+ }
48256
+ if (isNodeOfType(node, "JSXMemberExpression")) {
48257
+ if (!isNodeOfType(node.property, "JSXIdentifier")) return null;
48258
+ return {
48259
+ object: node.object,
48260
+ propertyName: node.property.name
48261
+ };
48262
+ }
48263
+ return null;
48264
+ };
48265
+ const isNamespaceMemberFromModule = (node, moduleName, memberNames, scopes) => {
48266
+ const memberParts = getStaticMemberParts(node);
48267
+ if (!memberParts || !memberNames.has(memberParts.propertyName)) return false;
48268
+ const namespaceSymbol = resolveImportedIdentifier(memberParts.object, scopes);
48269
+ return Boolean(namespaceSymbol && isImportFromModule(namespaceSymbol, moduleName) && isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier"));
48270
+ };
48271
+ const isReactMarkdownComponent = (node, scopes) => {
48272
+ if (isNodeOfType(node, "JSXIdentifier")) {
48273
+ const symbol = resolveImportedIdentifier(node, scopes);
48274
+ if (!symbol || !isImportFromModule(symbol, REACT_MARKDOWN_MODULE)) return false;
48275
+ if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return true;
48276
+ const importedName = getImportedName(symbol.declarationNode);
48277
+ return importedName === "default" || Boolean(importedName && REACT_MARKDOWN_NAMED_EXPORTS.has(importedName));
48278
+ }
48279
+ return isNamespaceMemberFromModule(node, REACT_MARKDOWN_MODULE, REACT_MARKDOWN_NAMESPACE_EXPORTS, scopes);
48280
+ };
48281
+ const isPluginFromModule = (rawNode, moduleName, scopes, visitedSymbolIds) => {
48282
+ const node = stripParenExpression(rawNode);
48283
+ if (isNodeOfType(node, "Identifier")) {
48284
+ const symbol = resolveConstIdentifierAlias(node, scopes);
48285
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
48286
+ if (isDefaultImportSymbol(symbol, moduleName)) return true;
48287
+ if (symbol.kind !== "const" || !symbol.initializer) return false;
48288
+ visitedSymbolIds.add(symbol.id);
48289
+ return isPluginFromModule(symbol.initializer, moduleName, scopes, visitedSymbolIds);
48290
+ }
48291
+ if (isNamespaceMemberFromModule(node, moduleName, DEFAULT_EXPORT_NAMES, scopes)) return true;
48292
+ if (!isNodeOfType(node, "ArrayExpression")) return false;
48293
+ for (const element of node.elements) {
48294
+ if (!element || isNodeOfType(element, "SpreadElement")) continue;
48295
+ return isPluginFromModule(element, moduleName, scopes, visitedSymbolIds);
48296
+ }
48297
+ return false;
48298
+ };
48299
+ const collectPluginEntries = (rawNode, scopes, visitedSymbolIds) => {
48300
+ const node = stripParenExpression(rawNode);
48301
+ if (isNodeOfType(node, "Identifier")) {
48302
+ const symbol = scopes.referenceFor(node)?.resolvedSymbol;
48303
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
48304
+ visitedSymbolIds.add(symbol.id);
48305
+ return collectPluginEntries(symbol.initializer, scopes, visitedSymbolIds);
48306
+ }
48307
+ if (!isNodeOfType(node, "ArrayExpression")) return null;
48308
+ const entries = [];
48309
+ for (const element of node.elements) {
48310
+ if (!element) continue;
48311
+ if (isNodeOfType(element, "SpreadElement")) {
48312
+ const spreadEntries = collectPluginEntries(element.argument, scopes, new Set(visitedSymbolIds));
48313
+ if (spreadEntries === null) return null;
48314
+ entries.push(...spreadEntries);
48315
+ continue;
48316
+ }
48317
+ entries.push(element);
48318
+ }
48319
+ return entries;
48320
+ };
48321
+ const findEffectiveExplicitAttribute = (attributes, attributeName) => {
48322
+ for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
48323
+ const attribute = attributes[attributeIndex];
48324
+ if (!attribute || isNodeOfType(attribute, "JSXSpreadAttribute")) return null;
48325
+ if (isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && attribute.name.name === attributeName) return attribute;
48326
+ }
48327
+ return null;
48328
+ };
48329
+ const getAttributeExpression = (attribute) => {
48330
+ if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
48331
+ return isNodeOfType(attribute.value.expression, "JSXEmptyExpression") ? null : attribute.value.expression;
48332
+ };
48333
+ const isDomPurifyNamespace = (node, scopes) => {
48334
+ const symbol = resolveImportedIdentifier(node, scopes);
48335
+ if (!symbol) return false;
48336
+ const importDeclaration = getImportDeclaration(symbol);
48337
+ if (!importDeclaration || !DOMPURIFY_MODULES.has(String(importDeclaration.source.value))) return false;
48338
+ return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
48339
+ };
48340
+ const isDomPurifySanitizeCall = (node, scopes) => {
48341
+ if (!isNodeOfType(node, "CallExpression")) return false;
48342
+ const memberParts = getStaticMemberParts(stripParenExpression(node.callee));
48343
+ return Boolean(memberParts && memberParts.propertyName === "sanitize" && isDomPurifyNamespace(memberParts.object, scopes));
48344
+ };
48345
+ const isStaticOrSanitizedMarkdownExpression = (rawNode, scopes, visitedSymbolIds) => {
48346
+ const node = stripParenExpression(rawNode);
48347
+ if (isNodeOfType(node, "Literal")) return true;
48348
+ if (isNodeOfType(node, "TemplateLiteral")) return node.expressions.every((expression) => isStaticOrSanitizedMarkdownExpression(expression, scopes, new Set(visitedSymbolIds)));
48349
+ if (isDomPurifySanitizeCall(node, scopes)) return true;
48350
+ if (isNodeOfType(node, "ConditionalExpression")) return isStaticOrSanitizedMarkdownExpression(node.consequent, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.alternate, scopes, new Set(visitedSymbolIds));
48351
+ if (isNodeOfType(node, "BinaryExpression") && node.operator === "+") return isStaticOrSanitizedMarkdownExpression(node.left, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.right, scopes, new Set(visitedSymbolIds));
48352
+ if (!isNodeOfType(node, "Identifier")) return false;
48353
+ const symbol = scopes.referenceFor(node)?.resolvedSymbol;
48354
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
48355
+ visitedSymbolIds.add(symbol.id);
48356
+ return isStaticOrSanitizedMarkdownExpression(symbol.initializer, scopes, visitedSymbolIds);
48357
+ };
48358
+ const hasDynamicUnsanitizedChildren = (openingElement, scopes) => {
48359
+ const jsxElement = openingElement.parent;
48360
+ if (!isNodeOfType(jsxElement, "JSXElement")) return false;
48361
+ const meaningfulChildren = jsxElement.children.filter((child) => !isNodeOfType(child, "JSXText") || child.value.trim().length > 0);
48362
+ if (meaningfulChildren.length > 0) return meaningfulChildren.some((child) => {
48363
+ if (isNodeOfType(child, "JSXText")) return false;
48364
+ if (!isNodeOfType(child, "JSXExpressionContainer")) return true;
48365
+ if (isNodeOfType(child.expression, "JSXEmptyExpression")) return false;
48366
+ return !isStaticOrSanitizedMarkdownExpression(child.expression, scopes, /* @__PURE__ */ new Set());
48367
+ });
48368
+ const childrenAttribute = findEffectiveExplicitAttribute(openingElement.attributes, "children");
48369
+ if (!childrenAttribute) return false;
48370
+ const childrenExpression = getAttributeExpression(childrenAttribute);
48371
+ return Boolean(childrenExpression && !isStaticOrSanitizedMarkdownExpression(childrenExpression, scopes, /* @__PURE__ */ new Set()));
48372
+ };
48373
+ const reactMarkdownUnsanitizedRawHtml = defineRule({
48374
+ id: "react-markdown-unsanitized-raw-html",
48375
+ title: "Unsanitized raw HTML in React Markdown",
48376
+ severity: "warn",
48377
+ recommendation: "Add `rehype-sanitize` to `rehypePlugins` or sanitize dynamic markdown before rendering. `skipHtml` does not disable HTML already parsed by `rehype-raw`.",
48378
+ create: skipNonProductionFiles((context) => ({ JSXOpeningElement(node) {
48379
+ if (!isReactMarkdownComponent(node.name, context.scopes)) return;
48380
+ const pluginsAttribute = findEffectiveExplicitAttribute(node.attributes, "rehypePlugins");
48381
+ if (!pluginsAttribute) return;
48382
+ const pluginsExpression = getAttributeExpression(pluginsAttribute);
48383
+ if (!pluginsExpression) return;
48384
+ const pluginEntries = collectPluginEntries(pluginsExpression, context.scopes, /* @__PURE__ */ new Set());
48385
+ if (pluginEntries === null) return;
48386
+ if (!pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_RAW_MODULE, context.scopes, /* @__PURE__ */ new Set()))) return;
48387
+ if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
48388
+ context.report({
48389
+ node,
48390
+ message: MESSAGE$6
48391
+ });
48392
+ } }))
48393
+ });
48394
+ //#endregion
47575
48395
  //#region src/plugin/utils/collect-react-redux-selector-aliases.ts
47576
48396
  const REACT_REDUX_MODULE = "react-redux";
47577
48397
  const collectReactReduxSelectorAliases = (programRoot) => {
@@ -50468,7 +51288,7 @@ const isReactNativeDimensionsCallee = (node, callee) => {
50468
51288
  };
50469
51289
  const STYLE_FACTORY_CALLEE_PATTERN$1 = /(?:^|\.)(?:make|create)(?:Use)?Styles$/;
50470
51290
  const isInsideStyleFactoryCallback = (node) => {
50471
- const enclosingFunction = findEnclosingFunction(node);
51291
+ const enclosingFunction = findEnclosingFunction$1(node);
50472
51292
  if (!enclosingFunction) return false;
50473
51293
  const callExpression = enclosingFunction.parent;
50474
51294
  if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) return false;
@@ -57324,12 +58144,14 @@ const declarationAwaitsRequestScopedCall = (declaration) => {
57324
58144
  }
57325
58145
  return false;
57326
58146
  };
57327
- const declarationAwaitsGate = (declaration) => {
58147
+ const declarationAwaitsGate = (declaration, context) => {
57328
58148
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
57329
58149
  for (const declarator of declaration.declarations ?? []) {
57330
58150
  if (!isNodeOfType(declarator.init, "AwaitExpression")) continue;
57331
58151
  const argument = declarator.init.argument;
57332
58152
  if (!isNodeOfType(argument, "CallExpression")) continue;
58153
+ if (hasPossibleStaticMemberCallWrite(argument, context.scopes)) return true;
58154
+ if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) continue;
57333
58155
  const calleeName = getCalleeName$2(argument);
57334
58156
  if (!calleeName) continue;
57335
58157
  if (isAuthGuardName(calleeName)) return true;
@@ -57357,7 +58179,7 @@ const serverSequentialIndependentAwait = defineRule({
57357
58179
  if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
57358
58180
  if (declarationAwaitsExistingPromise(nextStatement)) continue;
57359
58181
  if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
57360
- if (declarationAwaitsGate(currentStatement)) continue;
58182
+ if (declarationAwaitsGate(currentStatement, context)) continue;
57361
58183
  context.report({
57362
58184
  node: nextStatement,
57363
58185
  message: "This await doesn't use the previous result, so your users wait twice as long for nothing."
@@ -58343,7 +59165,7 @@ const tanstackStartNoNavigateInRender = defineRule({
58343
59165
  const isReturnedFromCustomHook = (functionNode) => {
58344
59166
  const parent = functionNode.parent;
58345
59167
  if (isNodeOfType(parent, "ReturnStatement")) {
58346
- const outerFunction = findEnclosingFunction(parent);
59168
+ const outerFunction = findEnclosingFunction$1(parent);
58347
59169
  const hookName = outerFunction ? getFunctionBindingName$1(outerFunction) : null;
58348
59170
  return Boolean(hookName && HOOK_NAME_PATTERN$3.test(hookName));
58349
59171
  }
@@ -58360,13 +59182,13 @@ const tanstackStartNoNavigateInRender = defineRule({
58360
59182
  return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
58361
59183
  };
58362
59184
  const isDeferredNavigateCall = (callNode) => {
58363
- let enclosingFunction = findEnclosingFunction(callNode);
59185
+ let enclosingFunction = findEnclosingFunction$1(callNode);
58364
59186
  while (enclosingFunction) {
58365
59187
  if (isDeferredCallbackPosition(enclosingFunction)) return true;
58366
59188
  if (isWiredAsEventHandler(enclosingFunction)) return true;
58367
59189
  if (isReturnedFromCustomHook(enclosingFunction)) return true;
58368
59190
  if (!isSynchronouslyInvokedAnonymousWrapper(enclosingFunction)) return false;
58369
- enclosingFunction = findEnclosingFunction(enclosingFunction);
59191
+ enclosingFunction = findEnclosingFunction$1(enclosingFunction);
58370
59192
  }
58371
59193
  return false;
58372
59194
  };
@@ -62805,6 +63627,17 @@ const reactDoctorRules = [
62805
63627
  requires: [...new Set(["react", ...reactInJsxScope.requires ?? []])]
62806
63628
  }
62807
63629
  },
63630
+ {
63631
+ key: "react-doctor/react-markdown-unsanitized-raw-html",
63632
+ id: "react-markdown-unsanitized-raw-html",
63633
+ source: "react-doctor",
63634
+ originallyExternal: false,
63635
+ rule: {
63636
+ ...reactMarkdownUnsanitizedRawHtml,
63637
+ framework: "global",
63638
+ category: "Security"
63639
+ }
63640
+ },
62808
63641
  {
62809
63642
  key: "react-doctor/redux-useselector-inline-derivation",
62810
63643
  id: "redux-useselector-inline-derivation",