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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +0 -46
  2. package/dist/index.js +724 -1834
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,13 +7,6 @@ import { analyze } from "eslint-scope";
7
7
  //#region src/plugin/utils/is-node-of-type.ts
8
8
  const isNodeOfType = (node, type) => node !== null && typeof node === "object" && "type" in node && node.type === type;
9
9
  //#endregion
10
- //#region src/plugin/utils/is-type-only-import.ts
11
- const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
12
- if (!specifiers || specifiers.length === 0) return false;
13
- return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
14
- };
15
- const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
16
- //#endregion
17
10
  //#region src/plugin/utils/non-react-jsx-dialect.ts
18
11
  const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
19
12
  "solid-js",
@@ -28,33 +21,17 @@ const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
28
21
  "vidode"
29
22
  ]);
30
23
  const NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES = ["solid-js", "@builder.io/qwik"];
31
- const REACT_JSX_DIALECT_PACKAGE_PREFIXES = [
32
- "react",
33
- "react-dom",
34
- "preact"
35
- ];
36
24
  const startsWithAny = (source, prefixes) => prefixes.some((prefix) => source === prefix || source.startsWith(`${prefix}/`));
37
- const collectJsxRuntimeImports = (program) => {
38
- let hasNonReactRuntime = false;
39
- let hasReactRuntime = false;
25
+ const fileImportsNonReactJsxDialect = (program) => {
40
26
  for (const statement of program.body) {
41
27
  if (!isNodeOfType(statement, "ImportDeclaration")) continue;
42
- const importDeclaration = statement;
43
- if (isTypeOnlyImport(importDeclaration)) continue;
44
- const source = importDeclaration.source;
28
+ const source = statement.source;
45
29
  const value = source && typeof source.value === "string" ? source.value : null;
46
30
  if (!value) continue;
47
- if (NON_REACT_JSX_DIALECT_PACKAGES.has(value) || startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) hasNonReactRuntime = true;
48
- if (startsWithAny(value, REACT_JSX_DIALECT_PACKAGE_PREFIXES)) hasReactRuntime = true;
31
+ if (NON_REACT_JSX_DIALECT_PACKAGES.has(value)) return true;
32
+ if (startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) return true;
49
33
  }
50
- return {
51
- hasNonReactRuntime,
52
- hasReactRuntime
53
- };
54
- };
55
- const fileImportsNonReactJsxDialect = (program) => {
56
- const runtimeImports = collectJsxRuntimeImports(program);
57
- return runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
34
+ return false;
58
35
  };
59
36
  const jsxAttributeIsNonReactDialectMarker = (openingNode) => {
60
37
  for (const attribute of openingNode.attributes) {
@@ -267,7 +244,6 @@ const VISITOR_NODE_NAME_PATTERN = /^[A-Z]/;
267
244
  const wrapCreateForReactJsxOnly = (create) => ((context) => {
268
245
  const innerVisitors = create(context);
269
246
  let fileIsNonReactJsx = false;
270
- let fileImportsReactRuntime = false;
271
247
  const wrappedVisitors = {};
272
248
  for (const [key, visitor] of Object.entries(innerVisitors)) {
273
249
  if (typeof visitor !== "function") {
@@ -280,16 +256,14 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
280
256
  }
281
257
  if (key === "Program") {
282
258
  wrappedVisitors.Program = (node) => {
283
- const runtimeImports = collectJsxRuntimeImports(node);
284
- fileImportsReactRuntime = runtimeImports.hasReactRuntime;
285
- fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
259
+ fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
286
260
  visitor(node);
287
261
  };
288
262
  continue;
289
263
  }
290
264
  if (key === "JSXOpeningElement") {
291
265
  wrappedVisitors.JSXOpeningElement = (node) => {
292
- if (!fileImportsReactRuntime && !fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
266
+ if (!fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
293
267
  if (fileIsNonReactJsx) return;
294
268
  visitor(node);
295
269
  };
@@ -301,9 +275,7 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
301
275
  };
302
276
  }
303
277
  if (!("Program" in wrappedVisitors)) wrappedVisitors.Program = (node) => {
304
- const runtimeImports = collectJsxRuntimeImports(node);
305
- fileImportsReactRuntime = runtimeImports.hasReactRuntime;
306
- fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
278
+ fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
307
279
  };
308
280
  return wrappedVisitors;
309
281
  });
@@ -2340,7 +2312,7 @@ const anchorAmbiguousText = defineRule({
2340
2312
  });
2341
2313
  //#endregion
2342
2314
  //#region src/plugin/rules/a11y/anchor-has-content.ts
2343
- const MESSAGE$62 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2315
+ const MESSAGE$61 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2344
2316
  const isTransComponentsTemplate = (node) => {
2345
2317
  let current = node.parent;
2346
2318
  while (current) {
@@ -2376,7 +2348,7 @@ const anchorHasContent = defineRule({
2376
2348
  if (isTransComponentsTemplate(node)) return;
2377
2349
  context.report({
2378
2350
  node: opening.name,
2379
- message: MESSAGE$62
2351
+ message: MESSAGE$61
2380
2352
  });
2381
2353
  } };
2382
2354
  }
@@ -2808,7 +2780,7 @@ const parseJsxValue = (value) => {
2808
2780
  };
2809
2781
  //#endregion
2810
2782
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
2811
- const MESSAGE$61 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2783
+ const MESSAGE$60 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2812
2784
  const mayBeContentEditable = (node) => {
2813
2785
  const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
2814
2786
  if (!attribute) return false;
@@ -2839,7 +2811,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2839
2811
  if (tabIndexValue === null || tabIndexValue >= -1) return;
2840
2812
  context.report({
2841
2813
  node: node.name,
2842
- message: MESSAGE$61
2814
+ message: MESSAGE$60
2843
2815
  });
2844
2816
  return;
2845
2817
  }
@@ -2847,7 +2819,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2847
2819
  if (mayBeContentEditable(node)) return;
2848
2820
  context.report({
2849
2821
  node: node.name,
2850
- message: MESSAGE$61
2822
+ message: MESSAGE$60
2851
2823
  });
2852
2824
  } })
2853
2825
  });
@@ -4342,397 +4314,6 @@ const findTransparentExpressionRoot = (node) => {
4342
4314
  return current;
4343
4315
  };
4344
4316
  //#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
4736
4317
  //#region src/plugin/utils/is-inline-function-expression.ts
4737
4318
  /**
4738
4319
  * Type-guard for the two "inline function expression" ESTree forms:
@@ -4766,15 +4347,13 @@ const isIntentionalSequencingCallee = (callee) => {
4766
4347
  if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier")) return INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(callee.property.name);
4767
4348
  return false;
4768
4349
  };
4769
- const isAwaitingSleepLikeCall = (awaitNode, context) => {
4350
+ const isAwaitingSleepLikeCall = (awaitNode) => {
4770
4351
  if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
4771
4352
  const argument = awaitNode.argument;
4772
4353
  if (!argument) return false;
4773
4354
  if (!isNodeOfType(argument, "CallExpression")) return false;
4774
- if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) return false;
4775
4355
  return isIntentionalSequencingCallee(argument.callee);
4776
4356
  };
4777
- const isAwaitingPossiblyMutatedMemberCall = (awaitNode, context) => isNodeOfType(awaitNode, "AwaitExpression") && Boolean(awaitNode.argument) && hasPossibleStaticMemberCallWrite(awaitNode.argument, context.scopes);
4778
4357
  const PROMISE_CONCURRENCY_METHODS = new Set([
4779
4358
  "all",
4780
4359
  "allSettled",
@@ -4817,7 +4396,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
4817
4396
  });
4818
4397
  return isWaitLike;
4819
4398
  };
4820
- const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
4399
+ const isIntentionallySequentialAwait = (awaitNode) => isAwaitingSleepLikeCall(awaitNode) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
4821
4400
  const collectPatternIdentifiers = (pattern, target) => {
4822
4401
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
4823
4402
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -5008,12 +4587,12 @@ const getLoopLabelName = (loopNode) => {
5008
4587
  if (isNodeOfType(parent, "LabeledStatement") && isNodeOfType(parent.label, "Identifier")) return parent.label.name;
5009
4588
  return null;
5010
4589
  };
5011
- const loopBodyHasIntentionallySequentialAwait = (block, context) => {
4590
+ const loopBodyHasIntentionallySequentialAwait = (block) => {
5012
4591
  let foundIntentional = false;
5013
4592
  walkAst(block, (child) => {
5014
4593
  if (foundIntentional) return false;
5015
4594
  if (isInlineFunctionExpression(child) || isNodeOfType(child, "FunctionDeclaration")) return false;
5016
- if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child, context)) {
4595
+ if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child)) {
5017
4596
  foundIntentional = true;
5018
4597
  return false;
5019
4598
  }
@@ -5118,7 +4697,7 @@ const asyncAwaitInLoop = defineRule({
5118
4697
  const inspectLoop = (loopNode, label) => {
5119
4698
  const loopBody = loopNode.body;
5120
4699
  if (!loopBody) return;
5121
- if (loopBodyHasIntentionallySequentialAwait(loopBody, context)) return;
4700
+ if (loopBodyHasIntentionallySequentialAwait(loopBody)) return;
5122
4701
  if ((isNodeOfType(loopNode, "WhileStatement") || isNodeOfType(loopNode, "DoWhileStatement")) && isLoopTestDependentOnBodyState(loopNode.test, loopBody)) return;
5123
4702
  if (hasLoopCarriedDependency(loopBody)) return;
5124
4703
  if (loopBodyHasAwaitDependentEarlyExit(loopBody, getLoopLabelName(loopNode))) return;
@@ -5378,7 +4957,7 @@ const guardConsequentPerformsSideEffects = (consequent) => {
5378
4957
  });
5379
4958
  return performsSideEffects;
5380
4959
  };
5381
- const findEnclosingFunction = (node) => {
4960
+ const findEnclosingFunction$1 = (node) => {
5382
4961
  let ancestor = node.parent;
5383
4962
  while (ancestor) {
5384
4963
  if (isFunctionLike$2(ancestor)) return ancestor;
@@ -5391,7 +4970,7 @@ const guardTestReadsReassignedLocal = (test, guardStatement) => {
5391
4970
  const testIdentifierNames = /* @__PURE__ */ new Set();
5392
4971
  collectReferenceIdentifierNames(test, testIdentifierNames);
5393
4972
  if (testIdentifierNames.size === 0) return false;
5394
- const enclosingFunction = findEnclosingFunction(guardStatement);
4973
+ const enclosingFunction = findEnclosingFunction$1(guardStatement);
5395
4974
  if (!enclosingFunction || !isFunctionLike$2(enclosingFunction) || !enclosingFunction.body) return false;
5396
4975
  let readsReassignedLocal = false;
5397
4976
  walkAst(enclosingFunction.body, (child) => {
@@ -5562,36 +5141,19 @@ const isIntentionalSequencingAwait = (awaitedCall) => {
5562
5141
  return getCalleeIdentifierTrail(awaitedCall).some((name) => INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(name));
5563
5142
  };
5564
5143
  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
- };
5574
5144
  const isNonCallAwait = (statement) => {
5575
5145
  const awaitedExpression = getAwaitedCall(statement);
5576
5146
  if (!awaitedExpression) return false;
5577
5147
  const stripped = stripParenExpression(awaitedExpression);
5578
5148
  return !isNodeOfType(stripped, "CallExpression") && !isNodeOfType(stripped, "NewExpression") && !isNodeOfType(stripped, "ImportExpression");
5579
5149
  };
5580
- const sequenceContainsSerializationSignal = (statements, context) => {
5581
- let bareAwaitFunction = null;
5150
+ const sequenceContainsSerializationSignal = (statements) => {
5582
5151
  for (const statement of statements) {
5152
+ if (isBareExpressionAwait(statement)) return true;
5583
5153
  if (isNonCallAwait(statement)) return true;
5584
5154
  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
- }
5593
5155
  if (isOrderedUiFlowAwait(awaitedCall)) return true;
5594
- if (isIntentionalSequencingAwait(awaitedCall) && orderIndependentFunction === null) return true;
5156
+ if (isIntentionalSequencingAwait(awaitedCall)) return true;
5595
5157
  }
5596
5158
  return false;
5597
5159
  };
@@ -5649,7 +5211,7 @@ const asyncParallel = defineRule({
5649
5211
  const consecutiveAwaitStatements = [];
5650
5212
  const flushConsecutiveAwaits = () => {
5651
5213
  if (consecutiveAwaitStatements.length >= 3) {
5652
- if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements, context)) reportIfIndependent(consecutiveAwaitStatements, context);
5214
+ if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements)) reportIfIndependent(consecutiveAwaitStatements, context);
5653
5215
  }
5654
5216
  consecutiveAwaitStatements.length = 0;
5655
5217
  };
@@ -5662,7 +5224,7 @@ const asyncParallel = defineRule({
5662
5224
  });
5663
5225
  //#endregion
5664
5226
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
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.";
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.";
5666
5228
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
5667
5229
  const STORAGE_GLOBALS = new Set([
5668
5230
  "window",
@@ -5727,7 +5289,7 @@ const authTokenInWebStorage = defineRule({
5727
5289
  if (keyString === null || !isAuthCredentialKey(keyString)) return;
5728
5290
  context.report({
5729
5291
  node,
5730
- message: MESSAGE$60
5292
+ message: MESSAGE$59
5731
5293
  });
5732
5294
  },
5733
5295
  AssignmentExpression(node) {
@@ -5738,7 +5300,7 @@ const authTokenInWebStorage = defineRule({
5738
5300
  if (!propertyName || !isAuthCredentialKey(propertyName)) return;
5739
5301
  context.report({
5740
5302
  node: target,
5741
- message: MESSAGE$60
5303
+ message: MESSAGE$59
5742
5304
  });
5743
5305
  }
5744
5306
  }))
@@ -5877,7 +5439,7 @@ const CI_INSTALL_NEAR_SECRET_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b
5877
5439
  const INSTALL_COMMAND_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b/i;
5878
5440
  const SECRET_REFERENCE_PATTERN = /\bsecrets\.[A-Z0-9_]+/;
5879
5441
  const IGNORE_SCRIPTS_FLAG_PATTERN = /--ignore-scripts\b/;
5880
- const MESSAGE$59 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5442
+ const MESSAGE$58 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5881
5443
  const isWorkflowPath = (relativePath) => /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath);
5882
5444
  const scanWorkflowContent = (content) => {
5883
5445
  const lines = content.split("\n");
@@ -5922,7 +5484,7 @@ const scanWorkflowContent = (content) => {
5922
5484
  const installLineOffset = Math.max(step.lines.findIndex((stepLine) => INSTALL_COMMAND_PATTERN.test(stepLine)), 0);
5923
5485
  const installColumnIndex = step.lines[installLineOffset].search(INSTALL_COMMAND_PATTERN);
5924
5486
  return [{
5925
- message: MESSAGE$59,
5487
+ message: MESSAGE$58,
5926
5488
  line: step.startLineIndex + installLineOffset + 1,
5927
5489
  column: (installColumnIndex === -1 ? 0 : installColumnIndex) + 1
5928
5490
  }];
@@ -5932,7 +5494,7 @@ const scanWorkflowContent = (content) => {
5932
5494
  const scanNonWorkflowConfig = scanByPattern({
5933
5495
  shouldScan: (file) => isConfigOrCiPath(file.relativePath) && !file.relativePath.endsWith("package.json") && !isWorkflowPath(file.relativePath),
5934
5496
  pattern: CI_INSTALL_NEAR_SECRET_PATTERN,
5935
- message: MESSAGE$59
5497
+ message: MESSAGE$58
5936
5498
  });
5937
5499
  const scan = (file) => {
5938
5500
  if (isWorkflowPath(file.relativePath)) return scanWorkflowContent(file.content);
@@ -6427,6 +5989,21 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
6427
5989
  }
6428
5990
  });
6429
5991
  //#endregion
5992
+ //#region src/plugin/utils/get-static-property-key-name.ts
5993
+ const getStaticPropertyKeyName = (node, options = {}) => {
5994
+ if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition")) return null;
5995
+ if (node.computed) {
5996
+ if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
5997
+ return null;
5998
+ }
5999
+ if (isNodeOfType(node.key, "Identifier")) return node.key.name;
6000
+ if (isNodeOfType(node.key, "Literal")) {
6001
+ if (typeof node.key.value === "string") return node.key.value;
6002
+ if (options.stringifyNonStringLiterals) return String(node.key.value);
6003
+ }
6004
+ return null;
6005
+ };
6006
+ //#endregion
6430
6007
  //#region src/plugin/utils/are-expressions-structurally-equal.ts
6431
6008
  const areExpressionsStructurallyEqual = (a, b) => {
6432
6009
  if (!a || !b) return a === b;
@@ -6684,7 +6261,7 @@ const isPureEventBlockerHandler = (attribute) => {
6684
6261
  };
6685
6262
  //#endregion
6686
6263
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
6687
- const MESSAGE$58 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6264
+ const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6688
6265
  const KEY_HANDLERS = [
6689
6266
  "onKeyUp",
6690
6267
  "onKeyDown",
@@ -6895,7 +6472,7 @@ const clickEventsHaveKeyEvents = defineRule({
6895
6472
  if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6896
6473
  context.report({
6897
6474
  node: node.name,
6898
- message: MESSAGE$58
6475
+ message: MESSAGE$57
6899
6476
  });
6900
6477
  } };
6901
6478
  }
@@ -7110,6 +6687,15 @@ const getDirectConstInitializer = (symbol) => {
7110
6687
  return symbol.initializer;
7111
6688
  };
7112
6689
  //#endregion
6690
+ //#region src/plugin/utils/get-static-property-name.ts
6691
+ const getStaticPropertyName = (memberExpression) => {
6692
+ const property = memberExpression.property;
6693
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
6694
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
6695
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
6696
+ return null;
6697
+ };
6698
+ //#endregion
7113
6699
  //#region src/plugin/utils/get-range-start.ts
7114
6700
  const getRangeStart = (node) => {
7115
6701
  const rangeStart = node.range?.[0];
@@ -7505,6 +7091,16 @@ const collectFunctionReturnStatements = (functionNode) => {
7505
7091
  return returnStatements;
7506
7092
  };
7507
7093
  //#endregion
7094
+ //#region src/plugin/utils/find-enclosing-function.ts
7095
+ const findEnclosingFunction = (node) => {
7096
+ let cursor = node.parent;
7097
+ while (cursor) {
7098
+ if (isFunctionLike$2(cursor)) return cursor;
7099
+ cursor = cursor.parent ?? null;
7100
+ }
7101
+ return null;
7102
+ };
7103
+ //#endregion
7508
7104
  //#region src/plugin/utils/statement-always-exits.ts
7509
7105
  const statementAlwaysExits = (statement) => {
7510
7106
  if (isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement")) return true;
@@ -7550,8 +7146,8 @@ const applyDefinitions = (incomingDefinitions, definitions) => {
7550
7146
  const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow) => {
7551
7147
  if (!REASSIGNABLE_BINDING_KINDS.has(symbol.kind)) return symbol.initializer ? [symbol.initializer] : [];
7552
7148
  if (!controlFlow) return [];
7553
- const referenceFunction = findEnclosingFunction$1(referenceNode);
7554
- if (findEnclosingFunction$1(symbol.bindingIdentifier) !== referenceFunction) return [];
7149
+ const referenceFunction = findEnclosingFunction(referenceNode);
7150
+ if (findEnclosingFunction(symbol.bindingIdentifier) !== referenceFunction) return [];
7555
7151
  if (!referenceFunction) return [];
7556
7152
  const functionControlFlow = controlFlow.cfgFor(referenceFunction);
7557
7153
  if (!functionControlFlow) return [];
@@ -7575,7 +7171,7 @@ const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow)
7575
7171
  if (symbol.initializer) addDefinition(symbol.initializer, symbol.bindingIdentifier, bindingPosition);
7576
7172
  for (const reference of symbol.references) {
7577
7173
  const writePosition = getRangeStart(reference.identifier);
7578
- if (reference.flag === "read" || findEnclosingFunction$1(reference.identifier) !== referenceFunction || writePosition === null || writePosition >= referencePosition) continue;
7174
+ if (reference.flag === "read" || findEnclosingFunction(reference.identifier) !== referenceFunction || writePosition === null || writePosition >= referencePosition) continue;
7579
7175
  const assignedExpression = getAssignedExpressionForWrite(reference.identifier);
7580
7176
  if (!assignedExpression) continue;
7581
7177
  addDefinition(assignedExpression, reference.identifier, writePosition);
@@ -9129,7 +8725,7 @@ const getClassNameLiteral = (classAttribute) => {
9129
8725
  };
9130
8726
  //#endregion
9131
8727
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
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`.";
8728
+ const MESSAGE$56 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
9133
8729
  const NON_OPERABLE_ELEMENTS = new Set([
9134
8730
  "td",
9135
8731
  "th",
@@ -9247,7 +8843,6 @@ const HTML_FOR_ATTRIBUTE = "htmlFor";
9247
8843
  const LABEL_ELEMENT = "label";
9248
8844
  const LABEL_COMPONENT_NAME = "Label";
9249
8845
  const POLYMORPHIC_COMPONENT_PROP = "component";
9250
- const TITLE_ATTRIBUTE = "title";
9251
8846
  const WRAPPER_LABEL_PROP = "label";
9252
8847
  const SELECT_ELEMENT = "select";
9253
8848
  const DEFAULT_DEPTH = 5;
@@ -9293,96 +8888,6 @@ const hasNonEmptyPropValue = (attribute) => {
9293
8888
  }
9294
8889
  return true;
9295
8890
  };
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
- };
9386
8891
  const toAttributeMatchKey = (kind, value) => {
9387
8892
  const trimmedValue = value.trim();
9388
8893
  return trimmedValue.length > 0 ? `${kind}:${trimmedValue}` : null;
@@ -9639,7 +9144,6 @@ const controlHasAssociatedLabel = defineRule({
9639
9144
  if (typeValue === "submit" || typeValue === "reset") return;
9640
9145
  if (typeValue === "button" && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "value"))) return;
9641
9146
  }
9642
- if (isDomElement && hasNonEmptyNativeTitle(getLastJsxPropIgnoreCase(opening.attributes, TITLE_ATTRIBUTE), context.scopes)) return;
9643
9147
  if (supportsPlaceholderNameFallback(tagName, opening) && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "placeholder"))) return;
9644
9148
  if (hasLabellingProp(opening.attributes, settings.labelAttributes)) return;
9645
9149
  if (isInsideJsxAttribute(node)) return;
@@ -9659,7 +9163,7 @@ const controlHasAssociatedLabel = defineRule({
9659
9163
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
9660
9164
  context.report({
9661
9165
  node: candidate.opening,
9662
- message: MESSAGE$57
9166
+ message: MESSAGE$56
9663
9167
  });
9664
9168
  }
9665
9169
  }
@@ -10420,7 +9924,7 @@ const noVagueButtonLabel = defineRule({
10420
9924
  });
10421
9925
  //#endregion
10422
9926
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
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.";
9927
+ const MESSAGE$55 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
10424
9928
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
10425
9929
  const NAME_PROVIDING_ATTRIBUTES = [
10426
9930
  "aria-label",
@@ -10445,7 +9949,7 @@ const dialogHasAccessibleName = defineRule({
10445
9949
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
10446
9950
  context.report({
10447
9951
  node: node.name,
10448
- message: MESSAGE$56
9952
+ message: MESSAGE$55
10449
9953
  });
10450
9954
  } };
10451
9955
  }
@@ -10485,7 +9989,7 @@ const isEs6Component = (node) => {
10485
9989
  };
10486
9990
  //#endregion
10487
9991
  //#region src/plugin/rules/react-builtins/display-name.ts
10488
- const MESSAGE$55 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
9992
+ const MESSAGE$54 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
10489
9993
  const DEFAULT_ADDITIONAL_HOCS = [
10490
9994
  "observer",
10491
9995
  "lazy",
@@ -10678,7 +10182,7 @@ const displayName = defineRule({
10678
10182
  const reportAt = (node) => {
10679
10183
  context.report({
10680
10184
  node,
10681
- message: MESSAGE$55
10185
+ message: MESSAGE$54
10682
10186
  });
10683
10187
  };
10684
10188
  return {
@@ -11499,14 +11003,6 @@ const PROMISE_CHAIN_METHOD_NAMES$1 = new Set([
11499
11003
  "catch",
11500
11004
  "finally"
11501
11005
  ]);
11502
- const isPromiseChainCall = (callee) => isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && PROMISE_CHAIN_METHOD_NAMES$1.has(callee.property.name) && isNodeOfType(stripParenExpression(callee.object), "CallExpression");
11503
- const getPromiseChainCallForCallback = (candidate) => {
11504
- let callbackContainer = candidate.parent;
11505
- while (callbackContainer && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(callbackContainer.type)) callbackContainer = callbackContainer.parent;
11506
- if (!isNodeOfType(callbackContainer, "CallExpression")) return null;
11507
- if (!callbackContainer.arguments?.some((argument) => stripParenExpression(argument) === candidate)) return null;
11508
- return isPromiseChainCall(stripParenExpression(callbackContainer.callee)) ? callbackContainer : null;
11509
- };
11510
11006
  const collectEffectInvokedFunctions = (effectCallback) => {
11511
11007
  const invokedFunctions = new Set([effectCallback]);
11512
11008
  const localFunctionBindings = /* @__PURE__ */ new Map();
@@ -11518,6 +11014,7 @@ const collectEffectInvokedFunctions = (effectCallback) => {
11518
11014
  invokedFunctions.add(strippedCandidate);
11519
11015
  pendingFunctions.push(strippedCandidate);
11520
11016
  };
11017
+ const isPromiseChainCall = (callee) => isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && PROMISE_CHAIN_METHOD_NAMES$1.has(callee.property.name) && isNodeOfType(stripParenExpression(callee.object), "CallExpression");
11521
11018
  while (pendingFunctions.length > 0) {
11522
11019
  const currentFunction = pendingFunctions.pop();
11523
11020
  if (!currentFunction) break;
@@ -11589,7 +11086,7 @@ const componentOrHookDisplayNameForFunction = (functionNode) => {
11589
11086
  //#endregion
11590
11087
  //#region src/plugin/utils/enclosing-component-or-hook-name.ts
11591
11088
  const enclosingComponentOrHookName = (node) => {
11592
- const functionNode = findEnclosingFunction$1(node);
11089
+ const functionNode = findEnclosingFunction(node);
11593
11090
  return functionNode ? componentOrHookDisplayNameForFunction(functionNode) : null;
11594
11091
  };
11595
11092
  //#endregion
@@ -11646,11 +11143,11 @@ const executesDuringRender = (functionNode, scopes) => {
11646
11143
  //#endregion
11647
11144
  //#region src/plugin/utils/find-render-phase-component-or-hook.ts
11648
11145
  const findRenderPhaseComponentOrHook = (node, scopes) => {
11649
- let functionNode = findEnclosingFunction$1(node);
11146
+ let functionNode = findEnclosingFunction(node);
11650
11147
  while (functionNode) {
11651
11148
  if (componentOrHookDisplayNameForFunction(functionNode)) return functionNode;
11652
11149
  if (!executesDuringRender(functionNode, scopes)) return null;
11653
- functionNode = findEnclosingFunction$1(functionNode);
11150
+ functionNode = findEnclosingFunction(functionNode);
11654
11151
  }
11655
11152
  return null;
11656
11153
  };
@@ -11725,50 +11222,6 @@ const isCleanupReturningSubscribeLikeCallExpression = (node) => {
11725
11222
  return true;
11726
11223
  };
11727
11224
  //#endregion
11728
- //#region src/plugin/utils/is-node-reachable-within-function.ts
11729
- const isInsideStaticallyUnreachableBranch = (node) => {
11730
- let child = node;
11731
- let parent = node.parent;
11732
- while (parent) {
11733
- if (isNodeOfType(parent, "IfStatement") && isNodeOfType(parent.test, "Literal")) {
11734
- if (parent.test.value === false && parent.consequent === child) return true;
11735
- if (parent.test.value === true && parent.alternate === child) return true;
11736
- }
11737
- if (isNodeOfType(parent, "ConditionalExpression") && isNodeOfType(parent.test, "Literal")) {
11738
- if (parent.test.value === false && parent.consequent === child) return true;
11739
- if (parent.test.value === true && parent.alternate === child) return true;
11740
- }
11741
- if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) {
11742
- if (isNodeOfType(parent.left, "Literal") && (parent.operator === "&&" && !parent.left.value || parent.operator === "||" && Boolean(parent.left.value))) return true;
11743
- }
11744
- child = parent;
11745
- parent = parent.parent;
11746
- }
11747
- return false;
11748
- };
11749
- const isNodeReachableWithinFunction = (node, context) => {
11750
- if (isInsideStaticallyUnreachableBranch(node)) return false;
11751
- const owner = context.cfg.enclosingFunction(node);
11752
- if (!owner) return true;
11753
- const functionCfg = context.cfg.cfgFor(owner);
11754
- if (!functionCfg) return true;
11755
- const targetBlock = functionCfg.blockOf(node);
11756
- if (!targetBlock) return true;
11757
- const visitedBlocks = new Set([functionCfg.entry]);
11758
- const pendingBlocks = [functionCfg.entry];
11759
- while (pendingBlocks.length > 0) {
11760
- const currentBlock = pendingBlocks.pop();
11761
- if (!currentBlock) break;
11762
- if (currentBlock === targetBlock) return true;
11763
- for (const edge of currentBlock.successors) {
11764
- if (visitedBlocks.has(edge.to)) continue;
11765
- visitedBlocks.add(edge.to);
11766
- pendingBlocks.push(edge.to);
11767
- }
11768
- }
11769
- return false;
11770
- };
11771
- //#endregion
11772
11225
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
11773
11226
  const OBSERVER_REGISTRATION_METHOD_NAME = "observe";
11774
11227
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
@@ -11897,15 +11350,36 @@ const findSubscribeLikeUsages = (callback, context) => {
11897
11350
  });
11898
11351
  return usages.filter((usage) => isNodeReachableWithinFunction(usage.node, context));
11899
11352
  };
11353
+ const isNodeReachableWithinFunction = (node, context) => {
11354
+ const owner = context.cfg.enclosingFunction(node);
11355
+ if (!owner) return true;
11356
+ const functionCfg = context.cfg.cfgFor(owner);
11357
+ if (!functionCfg) return true;
11358
+ const targetBlock = functionCfg.blockOf(node);
11359
+ if (!targetBlock) return true;
11360
+ const visitedBlocks = new Set([functionCfg.entry]);
11361
+ const pendingBlocks = [functionCfg.entry];
11362
+ while (pendingBlocks.length > 0) {
11363
+ const currentBlock = pendingBlocks.pop();
11364
+ if (!currentBlock) break;
11365
+ if (currentBlock === targetBlock) return true;
11366
+ for (const edge of currentBlock.successors) {
11367
+ if (visitedBlocks.has(edge.to)) continue;
11368
+ visitedBlocks.add(edge.to);
11369
+ pendingBlocks.push(edge.to);
11370
+ }
11371
+ }
11372
+ return false;
11373
+ };
11900
11374
  const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, context) => {
11901
11375
  let pathAnchor = usageNode;
11902
- let pathOwner = findEnclosingFunction$1(pathAnchor);
11376
+ let pathOwner = findEnclosingFunction(pathAnchor);
11903
11377
  while (pathOwner && isSynchronousIteratorCallback(pathOwner)) {
11904
11378
  if (matchingNodes.length > 0 && matchingNodes.every((matchingNode) => context.cfg.enclosingFunction(matchingNode) === pathOwner)) break;
11905
11379
  const iteratorCall = pathOwner.parent;
11906
11380
  if (!isNodeOfType(iteratorCall, "CallExpression")) break;
11907
11381
  pathAnchor = iteratorCall;
11908
- pathOwner = findEnclosingFunction$1(pathAnchor);
11382
+ pathOwner = findEnclosingFunction(pathAnchor);
11909
11383
  }
11910
11384
  const owner = context.cfg.enclosingFunction(pathAnchor);
11911
11385
  if (!owner) return false;
@@ -12134,7 +11608,7 @@ const findCollectionMappingCall = (callbackNode) => {
12134
11608
  return callee.property.name === "map" && callNode.arguments?.[0] === callbackNode ? callNode : null;
12135
11609
  };
12136
11610
  const findMappedResourceCollectionKey = (resourceNode, context) => {
12137
- const callbackNode = findEnclosingFunction$1(resourceNode);
11611
+ const callbackNode = findEnclosingFunction(resourceNode);
12138
11612
  if (!callbackNode || !isNodeOfType(callbackNode, "ArrowFunctionExpression") && !isNodeOfType(callbackNode, "FunctionExpression")) return null;
12139
11613
  const mappingCall = findCollectionMappingCall(callbackNode);
12140
11614
  if (!mappingCall) return null;
@@ -12245,10 +11719,10 @@ const findSingleDirectInvocation = (functionNode, caller, context) => {
12245
11719
  });
12246
11720
  if (invocationCalls.length !== 1) return null;
12247
11721
  const invocationCall = invocationCalls[0];
12248
- return findEnclosingFunction$1(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
11722
+ return findEnclosingFunction(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
12249
11723
  };
12250
11724
  const resolveCleanupPathAnchor = (usageNode, effectCallback, context) => {
12251
- const usageFunction = findEnclosingFunction$1(usageNode);
11725
+ const usageFunction = findEnclosingFunction(usageNode);
12252
11726
  if (!usageFunction || usageFunction === effectCallback) return usageNode;
12253
11727
  return findSingleDirectInvocation(usageFunction, effectCallback, context) ?? usageNode;
12254
11728
  };
@@ -12263,7 +11737,7 @@ const resolveSingleAssignedCleanupFunction = (expression, usage, context) => {
12263
11737
  const assignmentReference = assignmentReferences[0];
12264
11738
  const assignmentTarget = findTransparentExpressionRoot(assignmentReference.identifier);
12265
11739
  const assignmentNode = assignmentTarget.parent;
12266
- if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction$1(assignmentNode) !== findEnclosingFunction$1(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [assignmentNode], context)) return null;
11740
+ if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction(assignmentNode) !== findEnclosingFunction(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [assignmentNode], context)) return null;
12267
11741
  const assignedValue = stripParenExpression(assignmentNode.right);
12268
11742
  return isFunctionLike$2(assignedValue) ? assignedValue : null;
12269
11743
  };
@@ -12352,12 +11826,12 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
12352
11826
  return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
12353
11827
  };
12354
11828
  const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
12355
- const componentFunction = findEnclosingFunction$1(callback);
11829
+ const componentFunction = findEnclosingFunction(callback);
12356
11830
  if (!componentFunction || !isNodeOfType(componentFunction, "ArrowFunctionExpression") && !isNodeOfType(componentFunction, "FunctionExpression") && !isNodeOfType(componentFunction, "FunctionDeclaration")) return false;
12357
11831
  let didFindUnmountCleanup = false;
12358
11832
  walkAst(componentFunction.body, (child) => {
12359
11833
  if (didFindUnmountCleanup) return false;
12360
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction) return;
11834
+ if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction) return;
12361
11835
  if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
12362
11836
  const dependencyList = child.arguments?.[1];
12363
11837
  if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
@@ -12370,98 +11844,10 @@ const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
12370
11844
  return didFindUnmountCleanup;
12371
11845
  };
12372
11846
  const hasSplitLifecycleCleanup = (callback, usage, context) => usage.handleKey !== null && hasRerunReleaseBeforeUsage(callback, usage, context) && hasStableUnmountCleanupForUsage(callback, usage, context);
12373
- const collectBlockingBooleanStates = (expression, blockedExpressionValue, context) => {
12374
- const unwrappedExpression = stripParenExpression(expression);
12375
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return collectBlockingBooleanStates(unwrappedExpression.argument, !blockedExpressionValue, context);
12376
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
12377
- if (!(unwrappedExpression.operator === "||" && blockedExpressionValue || unwrappedExpression.operator === "&&" && !blockedExpressionValue)) return [];
12378
- return [...collectBlockingBooleanStates(unwrappedExpression.left, blockedExpressionValue, context), ...collectBlockingBooleanStates(unwrappedExpression.right, blockedExpressionValue, context)];
12379
- }
12380
- if (isNodeOfType(unwrappedExpression, "BinaryExpression") && [
12381
- "===",
12382
- "==",
12383
- "!==",
12384
- "!="
12385
- ].includes(unwrappedExpression.operator)) {
12386
- const leftValue = readStaticBoolean(unwrappedExpression.left);
12387
- const rightValue = readStaticBoolean(unwrappedExpression.right);
12388
- const booleanValue = leftValue ?? rightValue;
12389
- const comparedKey = resolveExpressionKey(leftValue === null ? unwrappedExpression.left : unwrappedExpression.right, context);
12390
- if (booleanValue === null || comparedKey === null) return [];
12391
- return [{
12392
- key: comparedKey,
12393
- value: (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "==") === blockedExpressionValue ? booleanValue : !booleanValue
12394
- }];
12395
- }
12396
- const expressionKey = resolveExpressionKey(unwrappedExpression, context);
12397
- return expressionKey === null ? [] : [{
12398
- key: expressionKey,
12399
- value: blockedExpressionValue
12400
- }];
12401
- };
12402
- const isDirectEarlyReturnConsequent = (ifStatement) => {
12403
- if (!isNodeOfType(ifStatement, "IfStatement") || ifStatement.alternate) return false;
12404
- if (isNodeOfType(ifStatement.consequent, "ReturnStatement")) return true;
12405
- return isNodeOfType(ifStatement.consequent, "BlockStatement") && ifStatement.consequent.body.length === 1 && isNodeOfType(ifStatement.consequent.body[0], "ReturnStatement");
12406
- };
12407
- const collectDeferredUsageGuardStates = (callback, usageNode, context) => {
12408
- if (!isFunctionLike$2(callback) || callback.async) return [];
12409
- const guardStates = [];
12410
- walkAst(callback.body, (child) => {
12411
- if (child !== callback.body && isFunctionLike$2(child)) return false;
12412
- if (isNodeOfType(child, "IfStatement") && isDirectEarlyReturnConsequent(child) && doMatchingNodesCoverEveryPathBeforeUsage(usageNode, [child], callback, context)) guardStates.push(...collectBlockingBooleanStates(child.test, true, context));
12413
- });
12414
- let descendant = usageNode;
12415
- let ancestor = descendant.parent;
12416
- while (ancestor && ancestor !== callback) {
12417
- if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant) guardStates.push(...collectBlockingBooleanStates(ancestor.test, false, context));
12418
- descendant = ancestor;
12419
- ancestor = ancestor.parent;
12420
- }
12421
- return guardStates;
12422
- };
12423
- const cleanupReturnInvalidatesGuard = (cleanupReturn, guardState, context) => {
12424
- if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return false;
12425
- const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
12426
- if (!cleanupFunction || !isFunctionLike$2(cleanupFunction) || cleanupFunction.async) return false;
12427
- let didInvalidateGuard = false;
12428
- walkAst(cleanupFunction.body, (child) => {
12429
- if (didInvalidateGuard) return false;
12430
- if (child !== cleanupFunction.body && isFunctionLike$2(child)) return false;
12431
- if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveExpressionKey(child.left, context) === guardState.key && readStaticBoolean(child.right) === guardState.value && context.cfg.isUnconditionalFromEntry(child)) {
12432
- didInvalidateGuard = true;
12433
- return false;
12434
- }
12435
- });
12436
- return didInvalidateGuard;
12437
- };
12438
- const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, context) => {
12439
- const usageStart = getRangeStart(usageNode);
12440
- if (!isFunctionLike$2(callback) || usageStart === null) return true;
12441
- let didWriteGuard = false;
12442
- walkAst(callback.body, (child) => {
12443
- if (didWriteGuard) return false;
12444
- if (child !== callback.body && isFunctionLike$2(child)) return false;
12445
- const childStart = getRangeStart(child);
12446
- if (childStart === null || childStart >= usageStart) return;
12447
- const writtenExpression = isNodeOfType(child, "AssignmentExpression") ? child.left : isNodeOfType(child, "UpdateExpression") ? child.argument : null;
12448
- if (writtenExpression && resolveExpressionKey(writtenExpression, context) === guardState.key) {
12449
- didWriteGuard = true;
12450
- return false;
12451
- }
12452
- });
12453
- return didWriteGuard;
12454
- };
12455
- const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
12456
- const usageFunction = findEnclosingFunction$1(usage.node);
12457
- const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
12458
- if (usage.handleKey === null || !usageFunction || usageFunction === callback || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
12459
- return collectDeferredUsageGuardStates(usageFunction, usage.node, context).some((guardState) => !deferredUsageWritesGuardBeforeUsage(usageFunction, usage.node, guardState, context) && cleanupReturns.every((cleanupReturn) => cleanupReturnInvalidatesGuard(cleanupReturn, guardState, context)));
12460
- };
12461
11847
  const effectHasCleanupForUsage = (callback, usage, context) => {
12462
11848
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
12463
11849
  if (callback.async) return false;
12464
- if (usage.kind === "subscribe" && findEnclosingFunction$1(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
11850
+ if (usage.kind === "subscribe" && findEnclosingFunction(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
12465
11851
  if (!isNodeOfType(callback.body, "BlockStatement")) return callback.body === usage.node && isCleanupReturningSubscribeLikeCallExpression(callback.body);
12466
11852
  const matchingCleanupReturns = [];
12467
11853
  walkInsideStatementBlocks(callback.body, (child) => {
@@ -12497,7 +11883,6 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
12497
11883
  if (!cleanupFunction || !isFunctionLike$2(cleanupFunction)) return;
12498
11884
  if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
12499
11885
  });
12500
- if (hasGuardedDeferredCleanup(callback, usage, matchingCleanupReturns, context)) return true;
12501
11886
  return doMatchingNodesCoverEveryPathAfterUsage(resolveCleanupPathAnchor(usage.node, callback, context), matchingCleanupReturns, context);
12502
11887
  };
12503
11888
  const findFirstUsageWithoutCleanup = (callback, usages, context) => {
@@ -12630,7 +12015,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
12630
12015
  parentNode = currentNode.parent;
12631
12016
  }
12632
12017
  if (!isNodeOfType(parentNode, "ReturnStatement") || parentNode.argument !== currentNode) return false;
12633
- const effectCallback = findEnclosingFunction$1(parentNode);
12018
+ const effectCallback = findEnclosingFunction(parentNode);
12634
12019
  const effectCall = effectCallback?.parent;
12635
12020
  return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
12636
12021
  };
@@ -12640,13 +12025,13 @@ const isPotentiallyReachableFunction = (functionNode, context) => {
12640
12025
  if (!bindingIdentifier) return false;
12641
12026
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12642
12027
  if (!symbol) return false;
12643
- return symbol.references.some((reference) => findEnclosingFunction$1(reference.identifier) !== functionNode);
12028
+ return symbol.references.some((reference) => findEnclosingFunction(reference.identifier) !== functionNode);
12644
12029
  };
12645
12030
  const isReleaseReachableForUsage = (releaseNode, usage, context) => {
12646
12031
  if (!isNodeReachableWithinFunction(releaseNode, context)) return false;
12647
- const releaseFunction = findEnclosingFunction$1(releaseNode);
12032
+ const releaseFunction = findEnclosingFunction(releaseNode);
12648
12033
  if (!releaseFunction) return true;
12649
- if (releaseFunction === findEnclosingFunction$1(usage.node)) return true;
12034
+ if (releaseFunction === findEnclosingFunction(usage.node)) return true;
12650
12035
  return isPotentiallyReachableFunction(releaseFunction, context);
12651
12036
  };
12652
12037
  const fileContainsReleaseForUsage = (usage, context) => {
@@ -12829,10 +12214,10 @@ const cleanupFunctionReleasesRefOwnedUsage = (cleanupFunction, componentFunction
12829
12214
  }
12830
12215
  if (assignedKey !== storage.refCurrentKey) return;
12831
12216
  const assignedValue = stripParenExpression(child.right);
12832
- if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction$1(child) === cleanupFunction) return;
12217
+ if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction(child) === cleanupFunction) return;
12833
12218
  const assignedSessionProperties = (isNodeOfType(assignedValue, "ObjectExpression") ? assignedValue : null)?.properties ?? [];
12834
12219
  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);
12835
- if (findEnclosingFunction$1(child) !== retainedFunction || !storesMatchingHandler) {
12220
+ if (findEnclosingFunction(child) !== retainedFunction || !storesMatchingHandler) {
12836
12221
  hasUnsafeRefWrite = true;
12837
12222
  return false;
12838
12223
  }
@@ -12853,12 +12238,12 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
12853
12238
  return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
12854
12239
  };
12855
12240
  const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
12856
- const componentFunction = findEnclosingFunction$1(retainedFunction);
12241
+ const componentFunction = findEnclosingFunction(retainedFunction);
12857
12242
  if (!componentFunction || !isFunctionLike$2(componentFunction)) return false;
12858
12243
  let didFindCleanupEffect = false;
12859
12244
  walkAst(componentFunction.body, (child) => {
12860
12245
  if (didFindCleanupEffect) return false;
12861
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction || !isReactApiCall(child, "useEffect", context.scopes)) return;
12246
+ if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction || !isReactApiCall(child, "useEffect", context.scopes)) return;
12862
12247
  const effectStatement = findTransparentExpressionRoot(child).parent;
12863
12248
  if (!isNodeOfType(effectStatement, "ExpressionStatement") || effectStatement.parent !== componentFunction.body) return;
12864
12249
  const effectCallback = getEffectCallback(child);
@@ -13712,202 +13097,6 @@ const resolveExhaustiveDepsSettings = (settings) => {
13712
13097
  };
13713
13098
  };
13714
13099
  //#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
13911
13100
  //#region src/plugin/rules/react-builtins/exhaustive-deps-suppression.ts
13912
13101
  const DISABLE_COMMENT_RULE_NAME_PATTERN$1 = /(?:^|[\s,/])exhaustive-deps(?:$|[\s,:])/;
13913
13102
  const DISABLE_NEXT_LINE_PATTERN$1 = /\b(?:eslint|oxlint)-disable-next-line\b([^\n]*)/;
@@ -13979,6 +13168,25 @@ const isExhaustiveDepsSuppressedAt = (filename, nodeStartOffset) => {
13979
13168
  return index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf16NewlineOffsets)) || index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf8NewlineOffsets));
13980
13169
  };
13981
13170
  //#endregion
13171
+ //#region src/plugin/utils/is-ast-descendant.ts
13172
+ /**
13173
+ * True when `inner` is `outer` itself or any descendant in the AST
13174
+ * `parent` chain. Walks `inner.parent` upward and stops at either a
13175
+ * match (`true`) or the chain's root (`false`).
13176
+ *
13177
+ * Was duplicated byte-identical in:
13178
+ * - exhaustive-deps-symbol-stability.ts
13179
+ * - semantic/closure-captures.ts
13180
+ */
13181
+ const isAstDescendant = (inner, outer) => {
13182
+ let current = inner;
13183
+ while (current) {
13184
+ if (current === outer) return true;
13185
+ current = current.parent ?? null;
13186
+ }
13187
+ return false;
13188
+ };
13189
+ //#endregion
13982
13190
  //#region src/plugin/rules/react-builtins/exhaustive-deps-symbol-stability.ts
13983
13191
  /**
13984
13192
  * Symbol-stability helpers consumed by the `exhaustive-deps` rule.
@@ -14159,7 +13367,6 @@ const EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS = new Set([
14159
13367
  "useLayoutEffect",
14160
13368
  "useInsertionEffect"
14161
13369
  ]);
14162
- const SOLE_WRITER_GUARD_HOOKS = new Set(["useEffect", "useLayoutEffect"]);
14163
13370
  const buildAdditionalHooksRegex = (additional) => {
14164
13371
  if (!additional) return null;
14165
13372
  try {
@@ -14296,7 +13503,7 @@ const stringifyMemberChain = (node) => {
14296
13503
  }
14297
13504
  return null;
14298
13505
  };
14299
- const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys, allowSoleWriterEffectGuards = false) => {
13506
+ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys) => {
14300
13507
  const keys = /* @__PURE__ */ new Set();
14301
13508
  const stableCapturedNames = /* @__PURE__ */ new Set();
14302
13509
  const moduleScopeCapturedNames = /* @__PURE__ */ new Set();
@@ -14307,10 +13514,6 @@ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys, allow
14307
13514
  const symbol = reference.resolvedSymbol;
14308
13515
  if (!symbol) continue;
14309
13516
  if (isRecursiveInitializerCapture(symbol, callback)) continue;
14310
- if (allowSoleWriterEffectGuards && isSoleWriterEffectGuardCapture(symbol, callback, scopes)) {
14311
- stableCapturedNames.add(symbol.name);
14312
- continue;
14313
- }
14314
13517
  if (symbolHasStableValue(symbol, scopes)) {
14315
13518
  stableCapturedNames.add(symbol.name);
14316
13519
  continue;
@@ -14983,7 +14186,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
14983
14186
  if (isNodeOfType(stripped, "Identifier")) declaredExactBindingKeys.add(key);
14984
14187
  declaredKeyToReportNode.set(key, elementNode);
14985
14188
  }
14986
- const { keys: captureKeys, stableCapturedNames, moduleScopeCapturedNames, outerFunctionCapturedNames } = collectCaptureDepKeys(callbackToAnalyze ?? callbackArgument, context.scopes, declaredExactBindingKeys, SOLE_WRITER_GUARD_HOOKS.has(hookName));
14189
+ const { keys: captureKeys, stableCapturedNames, moduleScopeCapturedNames, outerFunctionCapturedNames } = collectCaptureDepKeys(callbackToAnalyze ?? callbackArgument, context.scopes, declaredExactBindingKeys);
14987
14190
  for (const forcedCaptureKey of forcedCaptureKeys) captureKeys.add(forcedCaptureKey);
14988
14191
  addAggregatePropsDependency(captureKeys, declaredKeys, callbackToAnalyze ?? callbackArgument, context.scopes);
14989
14192
  const missingCaptureKeys = [];
@@ -15423,7 +14626,7 @@ const forbidElements = defineRule({
15423
14626
  });
15424
14627
  //#endregion
15425
14628
  //#region src/plugin/rules/react-builtins/forward-ref-uses-ref.ts
15426
- const MESSAGE$54 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
14629
+ const MESSAGE$53 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
15427
14630
  const forwardRefUsesRef = defineRule({
15428
14631
  id: "forward-ref-uses-ref",
15429
14632
  title: "forwardRef without ref parameter",
@@ -15446,7 +14649,7 @@ const forwardRefUsesRef = defineRule({
15446
14649
  if (isNodeOfType(onlyParam, "RestElement")) return;
15447
14650
  context.report({
15448
14651
  node: inner,
15449
- message: MESSAGE$54
14652
+ message: MESSAGE$53
15450
14653
  });
15451
14654
  } })
15452
14655
  });
@@ -15487,7 +14690,7 @@ const gitProviderUrlInjectionRisk = defineRule({
15487
14690
  });
15488
14691
  //#endregion
15489
14692
  //#region src/plugin/rules/a11y/heading-has-content.ts
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`.";
14693
+ const MESSAGE$52 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
15491
14694
  const DEFAULT_HEADING_TAGS = [
15492
14695
  "h1",
15493
14696
  "h2",
@@ -15521,7 +14724,7 @@ const headingHasContent = defineRule({
15521
14724
  for (const attribute of ["aria-label", "aria-labelledby"]) if (hasJsxPropIgnoreCase(node.attributes, attribute)) return;
15522
14725
  context.report({
15523
14726
  node,
15524
- message: MESSAGE$53
14727
+ message: MESSAGE$52
15525
14728
  });
15526
14729
  } };
15527
14730
  }
@@ -15685,7 +14888,7 @@ const hooksNoNanInDeps = defineRule({
15685
14888
  });
15686
14889
  //#endregion
15687
14890
  //#region src/plugin/rules/a11y/html-has-lang.ts
15688
- const MESSAGE$52 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
14891
+ const MESSAGE$51 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
15689
14892
  const resolveSettings$39 = (settings) => {
15690
14893
  const reactDoctor = settings?.["react-doctor"];
15691
14894
  return { htmlTags: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.htmlHasLang ?? {} : {}).htmlTags ?? ["html"] };
@@ -15732,13 +14935,13 @@ const htmlHasLang = defineRule({
15732
14935
  if (!lang) {
15733
14936
  context.report({
15734
14937
  node: node.name,
15735
- message: MESSAGE$52
14938
+ message: MESSAGE$51
15736
14939
  });
15737
14940
  return;
15738
14941
  }
15739
14942
  if (evaluateLang(lang.value) === "empty") context.report({
15740
14943
  node: lang,
15741
- message: MESSAGE$52
14944
+ message: MESSAGE$51
15742
14945
  });
15743
14946
  } };
15744
14947
  }
@@ -15978,7 +15181,7 @@ const htmlNoNestedInteractive = defineRule({
15978
15181
  });
15979
15182
  //#endregion
15980
15183
  //#region src/plugin/rules/a11y/iframe-has-title.ts
15981
- const MESSAGE$51 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15184
+ const MESSAGE$50 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
15982
15185
  const evaluateTitleValue = (value) => {
15983
15186
  if (!value) return "missing";
15984
15187
  if (isNodeOfType(value, "Literal")) {
@@ -16018,14 +15221,14 @@ const iframeHasTitle = defineRule({
16018
15221
  if (!titleAttr) {
16019
15222
  if (hasSpread || tag === "iframe") context.report({
16020
15223
  node: node.name,
16021
- message: MESSAGE$51
15224
+ message: MESSAGE$50
16022
15225
  });
16023
15226
  return;
16024
15227
  }
16025
15228
  const verdict = evaluateTitleValue(titleAttr.value);
16026
15229
  if (verdict === "missing" || verdict === "empty") context.report({
16027
15230
  node: titleAttr,
16028
- message: MESSAGE$51
15231
+ message: MESSAGE$50
16029
15232
  });
16030
15233
  } })
16031
15234
  });
@@ -16150,7 +15353,7 @@ const iframeMissingSandbox = defineRule({
16150
15353
  });
16151
15354
  //#endregion
16152
15355
  //#region src/plugin/rules/a11y/img-redundant-alt.ts
16153
- const MESSAGE$50 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
15356
+ const MESSAGE$49 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
16154
15357
  const DEFAULT_COMPONENTS = ["img"];
16155
15358
  const DEFAULT_REDUNDANT_WORDS = [
16156
15359
  "image",
@@ -16215,7 +15418,7 @@ const imgRedundantAlt = defineRule({
16215
15418
  if (!altAttribute) return;
16216
15419
  if (altValueRedundant(altAttribute, settings.words)) context.report({
16217
15420
  node: altAttribute,
16218
- message: MESSAGE$50
15421
+ message: MESSAGE$49
16219
15422
  });
16220
15423
  } };
16221
15424
  }
@@ -16797,14 +16000,14 @@ const isBindingInvokedOnRenderPath = (root, bindingName) => {
16797
16000
  if (didFindRenderPathInvocation) return false;
16798
16001
  if (!isNodeOfType(node, "CallExpression")) return;
16799
16002
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== bindingName) return;
16800
- let enclosingFunction = findEnclosingFunction$1(node);
16003
+ let enclosingFunction = findEnclosingFunction(node);
16801
16004
  while (enclosingFunction) {
16802
16005
  if (isDeferredCallbackPosition$1(enclosingFunction) || getHandlerNamedBindingName(enclosingFunction)) return;
16803
16006
  if (containingFunctionIsComponentOrHook(enclosingFunction)) {
16804
16007
  didFindRenderPathInvocation = true;
16805
16008
  return;
16806
16009
  }
16807
- enclosingFunction = findEnclosingFunction$1(enclosingFunction);
16010
+ enclosingFunction = findEnclosingFunction(enclosingFunction);
16808
16011
  }
16809
16012
  });
16810
16013
  return didFindRenderPathInvocation;
@@ -16839,7 +16042,7 @@ const jotaiSelectAtomInRenderBody = defineRule({
16839
16042
  };
16840
16043
  return { CallExpression(node) {
16841
16044
  if (!isImportedSelectAtom(node)) return;
16842
- const nearestFunctionLike = findEnclosingFunction$1(node);
16045
+ const nearestFunctionLike = findEnclosingFunction(node);
16843
16046
  if (!nearestFunctionLike) return;
16844
16047
  if (isDeferredCallback(nearestFunctionLike)) return;
16845
16048
  if (isBindingUsedAsHandler(nearestFunctionLike)) return;
@@ -17836,7 +17039,7 @@ const isPersistentCacheSetWrite = (callExpression, newExpression, enclosingFunct
17836
17039
  return !isAstDescendant(receiverBinding.scopeOwner, enclosingFunction);
17837
17040
  };
17838
17041
  const isInsideCacheMemo = (node) => {
17839
- const enclosingFunction = findEnclosingFunction$1(node);
17042
+ const enclosingFunction = findEnclosingFunction(node);
17840
17043
  let child = node;
17841
17044
  let cursor = node.parent ?? null;
17842
17045
  while (cursor) {
@@ -17874,7 +17077,7 @@ const getFunctionName = (functionNode) => {
17874
17077
  return null;
17875
17078
  };
17876
17079
  const isUncacheableOptionsMergeUtility = (node) => {
17877
- const enclosingFunction = findEnclosingFunction$1(node);
17080
+ const enclosingFunction = findEnclosingFunction(node);
17878
17081
  if (!enclosingFunction || !isFunctionLike$2(enclosingFunction)) return false;
17879
17082
  const functionName = getFunctionName(enclosingFunction);
17880
17083
  if (!functionName || isComponentOrHookName(functionName)) return false;
@@ -17998,7 +17201,7 @@ const GLOBAL_BUILTIN_NAMES = new Set([
17998
17201
  ]);
17999
17202
  const STRING_PROTOTYPE_PATH = "String.prototype";
18000
17203
  const REGEXP_PROTOTYPE_PATH = "RegExp.prototype";
18001
- const getStaticStringValue$1 = (argument) => {
17204
+ const getStaticStringValue = (argument) => {
18002
17205
  if (!argument) return null;
18003
17206
  const unwrappedArgument = stripParenExpression(argument);
18004
17207
  if (isNodeOfType(unwrappedArgument, "Literal") && typeof unwrappedArgument.value === "string") return unwrappedArgument.value;
@@ -18006,7 +17209,7 @@ const getStaticStringValue$1 = (argument) => {
18006
17209
  return null;
18007
17210
  };
18008
17211
  const getEffectiveRegExpFlags = (patternArgument, flagsArgument) => {
18009
- if (flagsArgument) return getStaticStringValue$1(flagsArgument);
17212
+ if (flagsArgument) return getStaticStringValue(flagsArgument);
18010
17213
  if (!patternArgument) return "";
18011
17214
  const unwrappedPattern = stripParenExpression(patternArgument);
18012
17215
  if (isNodeOfType(unwrappedPattern, "Literal") && unwrappedPattern.value instanceof RegExp) return unwrappedPattern.value.flags;
@@ -18065,6 +17268,13 @@ const isSafeStatefulReplaceAllSearch = (node, flags, context) => {
18065
17268
  const callee = stripParenExpression(replaceAllCall.callee);
18066
17269
  return isNodeOfType(callee, "MemberExpression") && !callee.computed && !callee.optional && !replaceAllCall.optional && isNodeOfType(callee.property, "Identifier") && callee.property.name === "replaceAll" && isProvenNativeStringReceiver(callee.object, context);
18067
17270
  };
17271
+ const getDestructuredBindingPropertyName = (bindingIdentifier) => {
17272
+ let bindingNode = bindingIdentifier;
17273
+ if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
17274
+ const property = bindingNode.parent;
17275
+ if (!isNodeOfType(property, "Property") || property.value !== bindingNode || !isNodeOfType(property.parent, "ObjectPattern")) return null;
17276
+ return getStaticPropertyKeyName(property, { allowComputedString: true });
17277
+ };
18068
17278
  const extendGlobalPath = (basePath, propertyName) => {
18069
17279
  if (basePath === "global") {
18070
17280
  if (propertyName === null || GLOBAL_OBJECT_NAMES.has(propertyName)) return "global";
@@ -18149,7 +17359,7 @@ const getCallRegExpHazard = (node, context, symbolCache) => {
18149
17359
  const mutationHazard = targetPath === "global" ? "globalRegExpReplaced" : "replaceAllIntegrityLost";
18150
17360
  const guardedPropertyName = targetPath === "global" ? "RegExp" : "replaceAll";
18151
17361
  if (isSinglePropertyMutation) {
18152
- const propertyName = getStaticStringValue$1(node.arguments?.[1]);
17362
+ const propertyName = getStaticStringValue(node.arguments?.[1]);
18153
17363
  return propertyName === null || propertyName === guardedPropertyName ? mutationHazard : "none";
18154
17364
  }
18155
17365
  if (!isPropertyCollectionMutation) return "none";
@@ -18178,7 +17388,7 @@ const getHoistableRegExpConstructionKind = (node, context) => {
18178
17388
  if (!STATEFUL_REGEXP_FLAGS_PATTERN.test(effectiveFlags)) return "stateless";
18179
17389
  return isSafeStatefulReplaceAllSearch(node, effectiveFlags, context) ? "statefulReplaceAll" : null;
18180
17390
  };
18181
- const MESSAGE$49 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
17391
+ const MESSAGE$48 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
18182
17392
  const jsHoistRegexp = defineRule({
18183
17393
  id: "js-hoist-regexp",
18184
17394
  title: "RegExp built inside a loop",
@@ -18195,7 +17405,7 @@ const jsHoistRegexp = defineRule({
18195
17405
  if (constructionKind === "statefulReplaceAll" && cachedEnvironmentHazard === "replaceAllIntegrityLost") return;
18196
17406
  context.report({
18197
17407
  node,
18198
- message: MESSAGE$49
17408
+ message: MESSAGE$48
18199
17409
  });
18200
17410
  };
18201
17411
  return createLoopAwareVisitors({
@@ -20815,7 +20025,7 @@ const jsxMaxDepth = defineRule({
20815
20025
  });
20816
20026
  //#endregion
20817
20027
  //#region src/plugin/rules/react-builtins/jsx-no-comment-textnodes.ts
20818
- const MESSAGE$48 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20028
+ const MESSAGE$47 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
20819
20029
  const LITERAL_TEXT_TAGS = new Set([
20820
20030
  "code",
20821
20031
  "pre",
@@ -20885,7 +20095,7 @@ const jsxNoCommentTextnodes = defineRule({
20885
20095
  if (isDeliberateStyledCommentToken(node)) return;
20886
20096
  context.report({
20887
20097
  node,
20888
- message: MESSAGE$48
20098
+ message: MESSAGE$47
20889
20099
  });
20890
20100
  } })
20891
20101
  });
@@ -20916,7 +20126,7 @@ const isInsideFunctionScope = (node) => {
20916
20126
  };
20917
20127
  //#endregion
20918
20128
  //#region src/plugin/rules/react-builtins/jsx-no-constructed-context-values.ts
20919
- const MESSAGE$47 = "Every reader of this context redraws on each render because you build its `value` inline.";
20129
+ const MESSAGE$46 = "Every reader of this context redraws on each render because you build its `value` inline.";
20920
20130
  const CONTEXT_MODULES$1 = [
20921
20131
  "react",
20922
20132
  "use-context-selector",
@@ -21014,7 +20224,7 @@ const jsxNoConstructedContextValues = defineRule({
21014
20224
  if (!isConstructedValue(innerExpression)) continue;
21015
20225
  context.report({
21016
20226
  node: attribute,
21017
- message: MESSAGE$47
20227
+ message: MESSAGE$46
21018
20228
  });
21019
20229
  }
21020
20230
  }
@@ -21869,7 +21079,7 @@ const DATA_ARRAY_PROP_SUFFIXES = [
21869
21079
  ];
21870
21080
  //#endregion
21871
21081
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop.ts
21872
- const MESSAGE$46 = "This child redraws every render because the prop gets a brand new array each time.";
21082
+ const MESSAGE$45 = "This child redraws every render because the prop gets a brand new array each time.";
21873
21083
  const isDataArrayPropName = (propName) => {
21874
21084
  if (DATA_ARRAY_PROP_NAMES.has(propName)) return true;
21875
21085
  for (const suffix of DATA_ARRAY_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -21956,7 +21166,7 @@ const jsxNoNewArrayAsProp = defineRule({
21956
21166
  if (!isArrayProducingExpression(expressionNode) && !followsRenderLocalArrayBinding(expressionNode, node)) return;
21957
21167
  context.report({
21958
21168
  node,
21959
- message: MESSAGE$46
21169
+ message: MESSAGE$45
21960
21170
  });
21961
21171
  }
21962
21172
  };
@@ -22214,7 +21424,7 @@ const SAFE_RECEIVER_NAMES = new Set([
22214
21424
  ]);
22215
21425
  //#endregion
22216
21426
  //#region src/plugin/rules/react-builtins/jsx-no-new-function-as-prop.ts
22217
- const MESSAGE$45 = "This child redraws every render because the prop gets a brand new function each time.";
21427
+ const MESSAGE$44 = "This child redraws every render because the prop gets a brand new function each time.";
22218
21428
  const isAccessorPredicateName = (propName) => {
22219
21429
  for (const prefix of ACCESSOR_PREDICATE_PREFIXES) {
22220
21430
  if (propName.length <= prefix.length) continue;
@@ -22420,7 +21630,7 @@ const jsxNoNewFunctionAsProp = defineRule({
22420
21630
  if (!isFunctionProducingExpression(expressionNode) && !followsRenderLocalFunctionBinding(expressionNode, node)) return;
22421
21631
  context.report({
22422
21632
  node,
22423
- message: MESSAGE$45
21633
+ message: MESSAGE$44
22424
21634
  });
22425
21635
  }
22426
21636
  };
@@ -22640,7 +21850,7 @@ const CONFIG_OBJECT_PROP_SUFFIXES = [
22640
21850
  ];
22641
21851
  //#endregion
22642
21852
  //#region src/plugin/rules/react-builtins/jsx-no-new-object-as-prop.ts
22643
- const MESSAGE$44 = "This child redraws every render because the prop gets a brand new object each time.";
21853
+ const MESSAGE$43 = "This child redraws every render because the prop gets a brand new object each time.";
22644
21854
  const isConfigObjectPropName = (propName) => {
22645
21855
  if (CONFIG_OBJECT_PROP_NAMES.has(propName)) return true;
22646
21856
  for (const suffix of CONFIG_OBJECT_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -22729,7 +21939,7 @@ const jsxNoNewObjectAsProp = defineRule({
22729
21939
  if (!isObjectProducingExpression(expressionNode) && !followsRenderLocalObjectBinding(expressionNode, node)) return;
22730
21940
  context.report({
22731
21941
  node,
22732
- message: MESSAGE$44
21942
+ message: MESSAGE$43
22733
21943
  });
22734
21944
  }
22735
21945
  };
@@ -22737,7 +21947,7 @@ const jsxNoNewObjectAsProp = defineRule({
22737
21947
  });
22738
21948
  //#endregion
22739
21949
  //#region src/plugin/rules/react-builtins/jsx-no-script-url.ts
22740
- const MESSAGE$43 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
21950
+ const MESSAGE$42 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
22741
21951
  const JAVASCRIPT_URL_PATTERN = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
22742
21952
  const resolveSettings$29 = (settings) => {
22743
21953
  const reactDoctor = settings?.["react-doctor"];
@@ -22775,7 +21985,7 @@ const jsxNoScriptUrl = defineRule({
22775
21985
  if (!value || !isNodeOfType(value, "Literal") || typeof value.value !== "string") continue;
22776
21986
  if (JAVASCRIPT_URL_PATTERN.test(value.value)) context.report({
22777
21987
  node: attribute,
22778
- message: MESSAGE$43
21988
+ message: MESSAGE$42
22779
21989
  });
22780
21990
  }
22781
21991
  } };
@@ -23395,7 +22605,7 @@ const jsxPropsNoSpreadMulti = defineRule({
23395
22605
  });
23396
22606
  //#endregion
23397
22607
  //#region src/plugin/rules/react-builtins/jsx-props-no-spreading.ts
23398
- const MESSAGE$42 = "You can't tell what props reach this element when you spread them.";
22608
+ const MESSAGE$41 = "You can't tell what props reach this element when you spread them.";
23399
22609
  const resolveSettings$25 = (settings) => {
23400
22610
  const reactDoctor = settings?.["react-doctor"];
23401
22611
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxPropsNoSpreading ?? {} : {};
@@ -23438,7 +22648,7 @@ const jsxPropsNoSpreading = defineRule({
23438
22648
  }
23439
22649
  context.report({
23440
22650
  node: attribute,
23441
- message: MESSAGE$42
22651
+ message: MESSAGE$41
23442
22652
  });
23443
22653
  didReportInFile = true;
23444
22654
  return;
@@ -23714,7 +22924,7 @@ const labelHasAssociatedControl = defineRule({
23714
22924
  });
23715
22925
  //#endregion
23716
22926
  //#region src/plugin/rules/a11y/lang.ts
23717
- const MESSAGE$41 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
22927
+ const MESSAGE$40 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
23718
22928
  const COMMON_LANGUAGE_PRIMARY_TAGS = new Set([
23719
22929
  "aa",
23720
22930
  "ab",
@@ -24194,7 +23404,7 @@ const lang = defineRule({
24194
23404
  if (expression.type === "Identifier" && expression.name === "undefined" || expression.type === "Literal" && expression.value === null) {
24195
23405
  context.report({
24196
23406
  node: langAttr,
24197
- message: MESSAGE$41
23407
+ message: MESSAGE$40
24198
23408
  });
24199
23409
  return;
24200
23410
  }
@@ -24203,7 +23413,7 @@ const lang = defineRule({
24203
23413
  if (value === null) return;
24204
23414
  if (!isValidLangTag(value)) context.report({
24205
23415
  node: langAttr,
24206
- message: MESSAGE$41
23416
+ message: MESSAGE$40
24207
23417
  });
24208
23418
  } })
24209
23419
  });
@@ -24254,7 +23464,7 @@ const mdxSsrExecutionRisk = defineRule({
24254
23464
  });
24255
23465
  //#endregion
24256
23466
  //#region src/plugin/rules/a11y/media-has-caption.ts
24257
- const MESSAGE$40 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
23467
+ const MESSAGE$39 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
24258
23468
  const DEFAULT_AUDIO = ["audio"];
24259
23469
  const DEFAULT_VIDEO = ["video"];
24260
23470
  const DEFAULT_TRACK = ["track"];
@@ -24343,7 +23553,7 @@ const mediaHasCaption = defineRule({
24343
23553
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
24344
23554
  context.report({
24345
23555
  node: node.name,
24346
- message: MESSAGE$40
23556
+ message: MESSAGE$39
24347
23557
  });
24348
23558
  return;
24349
23559
  }
@@ -24362,7 +23572,7 @@ const mediaHasCaption = defineRule({
24362
23572
  return kindValue.value.toLowerCase() === "captions";
24363
23573
  })) context.report({
24364
23574
  node: node.name,
24365
- message: MESSAGE$40
23575
+ message: MESSAGE$39
24366
23576
  });
24367
23577
  } };
24368
23578
  }
@@ -25905,7 +25115,7 @@ const nextjsNoVercelOgImport = defineRule({
25905
25115
  });
25906
25116
  //#endregion
25907
25117
  //#region src/plugin/rules/a11y/no-access-key.ts
25908
- const MESSAGE$39 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25118
+ const MESSAGE$38 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
25909
25119
  const isUndefinedIdentifier = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
25910
25120
  const noAccessKey = defineRule({
25911
25121
  id: "no-access-key",
@@ -25924,7 +25134,7 @@ const noAccessKey = defineRule({
25924
25134
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
25925
25135
  context.report({
25926
25136
  node: accessKey,
25927
- message: MESSAGE$39
25137
+ message: MESSAGE$38
25928
25138
  });
25929
25139
  return;
25930
25140
  }
@@ -25934,7 +25144,7 @@ const noAccessKey = defineRule({
25934
25144
  if (isUndefinedIdentifier(expression)) return;
25935
25145
  context.report({
25936
25146
  node: accessKey,
25937
- message: MESSAGE$39
25147
+ message: MESSAGE$38
25938
25148
  });
25939
25149
  }
25940
25150
  } };
@@ -26239,11 +25449,8 @@ const unwrapUseCallback = (node) => {
26239
25449
  const callee = node.callee;
26240
25450
  return isNodeOfType(callee, "Identifier") && callee.name === "useCallback" || isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "useCallback" ? node.arguments?.[0] : node;
26241
25451
  };
26242
- const hasParameterDefinition = (ref) => Boolean(ref.resolved?.defs.some((definition) => definition.type === "Parameter"));
26243
25452
  const resolveToFunction = (ref) => {
26244
- const definition = ref.resolved?.defs[0];
26245
- if (!definition || hasParameterDefinition(ref)) return null;
26246
- const definitionNode = definition.node;
25453
+ const definitionNode = ref.resolved?.defs[0]?.node;
26247
25454
  if (!definitionNode) return null;
26248
25455
  if (isFunctionLike$2(definitionNode)) return definitionNode;
26249
25456
  if (isNodeOfType(definitionNode, "VariableDeclarator")) {
@@ -26653,7 +25860,7 @@ const isCleanupReturnArgument = (analysis, node) => {
26653
25860
  if (isNodeOfType(node, "MemberExpression")) return true;
26654
25861
  if (isNodeOfType(node, "Identifier")) {
26655
25862
  const ref = getRef(analysis, node);
26656
- if (ref && (hasParameterDefinition(ref) || resolveToFunction(ref))) return true;
25863
+ if (ref && resolveToFunction(ref)) return true;
26657
25864
  }
26658
25865
  if (isNodeOfType(node, "ConditionalExpression")) return isCleanupReturnArgument(analysis, node.consequent) || isCleanupReturnArgument(analysis, node.alternate);
26659
25866
  return false;
@@ -26811,7 +26018,7 @@ const isIndependentWriterIdentifier = (componentFunction, identifier, includeDef
26811
26018
  };
26812
26019
  const hasUserInputSetterWriter = (setterRef, effectNode, includeDeferredWriters = false) => {
26813
26020
  if (!setterRef.resolved) return false;
26814
- const componentFunction = findEnclosingFunction$1(effectNode);
26021
+ const componentFunction = findEnclosingFunction(effectNode);
26815
26022
  if (!componentFunction) return false;
26816
26023
  for (const reference of setterRef.resolved.references) {
26817
26024
  if (reference.init) continue;
@@ -27125,7 +26332,7 @@ const resolveWrappedCallable = (analysis, node) => {
27125
26332
  if (isNodeOfType(candidate, "Identifier")) {
27126
26333
  const reference = getRef(analysis, candidate);
27127
26334
  if (!reference) return null;
27128
- if (reference.resolved?.defs.some((definition) => definition.type === "ImportBinding")) return null;
26335
+ if (reference.resolved?.defs.some((definition) => definition.type === "Parameter" || definition.type === "ImportBinding")) return null;
27129
26336
  const resolved = resolveToFunction(reference);
27130
26337
  if (resolved) return resolved;
27131
26338
  const definitionNode = reference.resolved?.defs[0]?.node;
@@ -27789,7 +26996,7 @@ const noAdjustStateOnPropChange = defineRule({
27789
26996
  });
27790
26997
  //#endregion
27791
26998
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
27792
- const MESSAGE$38 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
26999
+ 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.";
27793
27000
  const ALWAYS_FOCUSABLE_TAGS = new Set([
27794
27001
  "button",
27795
27002
  "embed",
@@ -27901,7 +27108,7 @@ const noAriaHiddenOnFocusable = defineRule({
27901
27108
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
27902
27109
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
27903
27110
  node: ariaHidden,
27904
- message: MESSAGE$38
27111
+ message: MESSAGE$37
27905
27112
  });
27906
27113
  } })
27907
27114
  });
@@ -28823,7 +28030,7 @@ const isAllLiteralArrayExpression = (node) => {
28823
28030
  };
28824
28031
  //#endregion
28825
28032
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
28826
- const MESSAGE$37 = "Your users can see & submit the wrong data when this list reorders.";
28033
+ const MESSAGE$36 = "Your users can see & submit the wrong data when this list reorders.";
28827
28034
  const SECOND_INDEX_METHODS = new Set([
28828
28035
  "every",
28829
28036
  "filter",
@@ -28942,14 +28149,14 @@ const noArrayIndexKey = defineRule({
28942
28149
  if (propName !== "key") continue;
28943
28150
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
28944
28151
  node: property,
28945
- message: MESSAGE$37
28152
+ message: MESSAGE$36
28946
28153
  });
28947
28154
  }
28948
28155
  } })
28949
28156
  });
28950
28157
  //#endregion
28951
28158
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
28952
- const MESSAGE$36 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
28159
+ 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.";
28953
28160
  const noAsyncEffectCallback = defineRule({
28954
28161
  id: "no-async-effect-callback",
28955
28162
  title: "Async effect callback",
@@ -28967,13 +28174,13 @@ const noAsyncEffectCallback = defineRule({
28967
28174
  if (!callback.async) return;
28968
28175
  context.report({
28969
28176
  node: callback,
28970
- message: MESSAGE$36
28177
+ message: MESSAGE$35
28971
28178
  });
28972
28179
  } })
28973
28180
  });
28974
28181
  //#endregion
28975
28182
  //#region src/plugin/rules/a11y/no-autofocus.ts
28976
- const MESSAGE$35 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
28183
+ 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.";
28977
28184
  const resolveSettings$21 = (settings) => {
28978
28185
  const reactDoctor = settings?.["react-doctor"];
28979
28186
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -29070,7 +28277,7 @@ const noAutofocus = defineRule({
29070
28277
  if (isConditionallyRendered(node)) return;
29071
28278
  context.report({
29072
28279
  node: autoFocusAttribute,
29073
- message: MESSAGE$35
28280
+ message: MESSAGE$34
29074
28281
  });
29075
28282
  } };
29076
28283
  }
@@ -29584,6 +28791,138 @@ const noBarrelImport = defineRule({
29584
28791
  }
29585
28792
  });
29586
28793
  //#endregion
28794
+ //#region src/plugin/utils/has-static-property-write-before.ts
28795
+ const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
28796
+ const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
28797
+ "CatchClause",
28798
+ "ConditionalExpression",
28799
+ "DoWhileStatement",
28800
+ "ForInStatement",
28801
+ "ForOfStatement",
28802
+ "ForStatement",
28803
+ "IfStatement",
28804
+ "LogicalExpression",
28805
+ "SwitchCase",
28806
+ "SwitchStatement",
28807
+ "TryStatement",
28808
+ "WhileStatement"
28809
+ ]);
28810
+ const getResolvedStaticPropertyName = (memberExpression, scopes) => {
28811
+ if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
28812
+ const directPropertyName = getStaticPropertyName(memberExpression);
28813
+ if (directPropertyName || !memberExpression.computed) return directPropertyName;
28814
+ const property = stripParenExpression(memberExpression.property);
28815
+ if (!isNodeOfType(property, "Identifier")) return null;
28816
+ const propertySymbol = resolveConstIdentifierAlias(property, scopes);
28817
+ const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
28818
+ return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
28819
+ };
28820
+ const collectScopeSymbols = (scope, symbols) => {
28821
+ symbols.push(...scope.symbols);
28822
+ for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
28823
+ };
28824
+ const getEquivalentSymbols = (identifier, scopes) => {
28825
+ const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
28826
+ if (!rootSymbol) return [];
28827
+ let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
28828
+ if (!symbolsByRootId) {
28829
+ symbolsByRootId = /* @__PURE__ */ new Map();
28830
+ equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
28831
+ }
28832
+ const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
28833
+ if (cachedSymbols) return cachedSymbols;
28834
+ const allSymbols = [];
28835
+ collectScopeSymbols(scopes.rootScope, allSymbols);
28836
+ const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
28837
+ symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
28838
+ return equivalentSymbols;
28839
+ };
28840
+ const findExecutionBoundary = (node) => {
28841
+ let current = node;
28842
+ while (current) {
28843
+ if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
28844
+ current = current.parent ?? null;
28845
+ }
28846
+ return null;
28847
+ };
28848
+ const isOnUnconditionalPath = (node, boundary) => {
28849
+ let current = node.parent ?? null;
28850
+ while (current && current !== boundary) {
28851
+ if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
28852
+ current = current.parent ?? null;
28853
+ }
28854
+ return current === boundary;
28855
+ };
28856
+ const findFunctionBindingIdentifier = (functionNode) => {
28857
+ if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
28858
+ const parent = functionNode.parent;
28859
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
28860
+ return null;
28861
+ };
28862
+ const findDirectCall = (identifier) => {
28863
+ let callee = identifier;
28864
+ let parent = callee.parent;
28865
+ while (parent && stripParenExpression(parent) === identifier) {
28866
+ callee = parent;
28867
+ parent = callee.parent;
28868
+ }
28869
+ return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
28870
+ };
28871
+ const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
28872
+ if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
28873
+ visitedFunctionNodes.add(functionNode);
28874
+ const referenceBoundary = findExecutionBoundary(referenceNode);
28875
+ if (!referenceBoundary) return false;
28876
+ const invocationCalls = [];
28877
+ const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
28878
+ if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
28879
+ const call = findDirectCall(reference.identifier);
28880
+ if (call) invocationCalls.push(call);
28881
+ }
28882
+ else {
28883
+ const call = findDirectCall(functionNode);
28884
+ if (call) invocationCalls.push(call);
28885
+ }
28886
+ return invocationCalls.some((call) => {
28887
+ if (call.range[0] >= referenceNode.range[0]) return false;
28888
+ const callBoundary = findExecutionBoundary(call);
28889
+ if (!callBoundary) return false;
28890
+ if (callBoundary === referenceBoundary) return true;
28891
+ if (!isFunctionLike$2(callBoundary)) return false;
28892
+ return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
28893
+ });
28894
+ };
28895
+ const isMemberWriteTarget = (memberExpression) => {
28896
+ const parent = memberExpression.parent;
28897
+ if (!parent) return false;
28898
+ if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
28899
+ if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
28900
+ return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
28901
+ };
28902
+ const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
28903
+ let parent = reference.identifier.parent;
28904
+ while (parent && stripParenExpression(parent) === reference.identifier) parent = parent.parent;
28905
+ if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== reference.identifier || getResolvedStaticPropertyName(parent, scopes) !== propertyName || !isMemberWriteTarget(parent)) return false;
28906
+ const writeBoundary = findExecutionBoundary(parent);
28907
+ const referenceBoundary = findExecutionBoundary(referenceNode);
28908
+ if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(parent, writeBoundary)) return false;
28909
+ if (writeBoundary === referenceBoundary) return parent.range[0] < referenceNode.range[0];
28910
+ return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
28911
+ });
28912
+ const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
28913
+ if (!isNodeOfType(identifier, "Identifier")) return false;
28914
+ return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
28915
+ };
28916
+ //#endregion
28917
+ //#region src/plugin/utils/has-symbol-write-before.ts
28918
+ const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
28919
+ if (reference.flag === "read") return false;
28920
+ const writeFunction = findEnclosingFunction(reference.identifier);
28921
+ if (writeFunction === findEnclosingFunction(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
28922
+ if (!writeFunction) return true;
28923
+ return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
28924
+ });
28925
+ //#endregion
29587
28926
  //#region src/plugin/utils/has-stable-call-target.ts
29588
28927
  const hasStableCallTarget = (callExpression, scopes) => {
29589
28928
  if (!isNodeOfType(callExpression, "CallExpression")) return false;
@@ -29911,7 +29250,7 @@ const noChainStateUpdates = defineRule({
29911
29250
  });
29912
29251
  //#endregion
29913
29252
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
29914
- const MESSAGE$34 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29253
+ const MESSAGE$33 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
29915
29254
  const noChildrenProp = defineRule({
29916
29255
  id: "no-children-prop",
29917
29256
  title: "Children passed as a prop",
@@ -29923,7 +29262,7 @@ const noChildrenProp = defineRule({
29923
29262
  if (node.name.name !== "children") return;
29924
29263
  context.report({
29925
29264
  node: node.name,
29926
- message: MESSAGE$34
29265
+ message: MESSAGE$33
29927
29266
  });
29928
29267
  },
29929
29268
  CallExpression(node) {
@@ -29936,7 +29275,7 @@ const noChildrenProp = defineRule({
29936
29275
  const propertyKey = property.key;
29937
29276
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
29938
29277
  node: propertyKey,
29939
- message: MESSAGE$34
29278
+ message: MESSAGE$33
29940
29279
  });
29941
29280
  }
29942
29281
  }
@@ -29944,7 +29283,7 @@ const noChildrenProp = defineRule({
29944
29283
  });
29945
29284
  //#endregion
29946
29285
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
29947
- const MESSAGE$33 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29286
+ const MESSAGE$32 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
29948
29287
  const noCloneElement = defineRule({
29949
29288
  id: "no-clone-element",
29950
29289
  title: "cloneElement makes child props fragile",
@@ -29957,7 +29296,7 @@ const noCloneElement = defineRule({
29957
29296
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
29958
29297
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
29959
29298
  node: callee,
29960
- message: MESSAGE$33
29299
+ message: MESSAGE$32
29961
29300
  });
29962
29301
  return;
29963
29302
  }
@@ -29970,7 +29309,7 @@ const noCloneElement = defineRule({
29970
29309
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
29971
29310
  context.report({
29972
29311
  node: callee,
29973
- message: MESSAGE$33
29312
+ message: MESSAGE$32
29974
29313
  });
29975
29314
  }
29976
29315
  } })
@@ -29990,7 +29329,7 @@ const getCallMethodName = (callee) => {
29990
29329
  };
29991
29330
  //#endregion
29992
29331
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
29993
- const MESSAGE$32 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29332
+ const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
29994
29333
  const CONTEXT_MODULES = [
29995
29334
  "react",
29996
29335
  "use-context-selector",
@@ -30038,13 +29377,13 @@ const noCreateContextInRender = defineRule({
30038
29377
  if (!componentOrHookName) return;
30039
29378
  context.report({
30040
29379
  node,
30041
- message: `${MESSAGE$32} (called inside "${componentOrHookName}")`
29380
+ message: `${MESSAGE$31} (called inside "${componentOrHookName}")`
30042
29381
  });
30043
29382
  } })
30044
29383
  });
30045
29384
  //#endregion
30046
29385
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
30047
- const MESSAGE$31 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
29386
+ 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.";
30048
29387
  const isUseMemoCallbackArgument = (functionNode) => {
30049
29388
  const parent = functionNode.parent;
30050
29389
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -30052,8 +29391,8 @@ const isUseMemoCallbackArgument = (functionNode) => {
30052
29391
  return isReactFunctionCall(parent, "useMemo");
30053
29392
  };
30054
29393
  const findEnclosingRenderFunction = (node) => {
30055
- let enclosingFunction = findEnclosingFunction$1(node);
30056
- while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
29394
+ let enclosingFunction = findEnclosingFunction(node);
29395
+ while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
30057
29396
  return enclosingFunction;
30058
29397
  };
30059
29398
  const noCreateRefInFunctionComponent = defineRule({
@@ -30074,7 +29413,7 @@ const noCreateRefInFunctionComponent = defineRule({
30074
29413
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes, context.cfg))) return;
30075
29414
  context.report({
30076
29415
  node,
30077
- message: MESSAGE$31
29416
+ message: MESSAGE$30
30078
29417
  });
30079
29418
  } })
30080
29419
  });
@@ -30214,7 +29553,7 @@ const noCreateStoreInRender = defineRule({
30214
29553
  });
30215
29554
  //#endregion
30216
29555
  //#region src/plugin/rules/react-builtins/no-danger.ts
30217
- const MESSAGE$30 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
29556
+ const MESSAGE$29 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
30218
29557
  const noDanger = defineRule({
30219
29558
  id: "no-danger",
30220
29559
  title: "Raw HTML injection can run unsafe markup",
@@ -30228,7 +29567,7 @@ const noDanger = defineRule({
30228
29567
  if (!propAttribute) return;
30229
29568
  context.report({
30230
29569
  node: propAttribute.name,
30231
- message: MESSAGE$30
29570
+ message: MESSAGE$29
30232
29571
  });
30233
29572
  },
30234
29573
  CallExpression(node) {
@@ -30240,7 +29579,7 @@ const noDanger = defineRule({
30240
29579
  const propertyKey = property.key;
30241
29580
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
30242
29581
  node: propertyKey,
30243
- message: MESSAGE$30
29582
+ message: MESSAGE$29
30244
29583
  });
30245
29584
  }
30246
29585
  }
@@ -30263,7 +29602,7 @@ const isMeaningfulJsxChild = (child) => {
30263
29602
  };
30264
29603
  //#endregion
30265
29604
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
30266
- const MESSAGE$29 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
29605
+ const MESSAGE$28 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
30267
29606
  const mergePropsShape = (target, source) => {
30268
29607
  target.hasDangerously ||= source.hasDangerously;
30269
29608
  target.hasChildren ||= source.hasChildren;
@@ -30338,7 +29677,7 @@ const noDangerWithChildren = defineRule({
30338
29677
  if (!hasChildrenProp && !hasNestedChildren) return;
30339
29678
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
30340
29679
  node: opening,
30341
- message: MESSAGE$29
29680
+ message: MESSAGE$28
30342
29681
  });
30343
29682
  },
30344
29683
  CallExpression(node) {
@@ -30351,7 +29690,7 @@ const noDangerWithChildren = defineRule({
30351
29690
  const positionalChildren = node.arguments.slice(2);
30352
29691
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
30353
29692
  node,
30354
- message: MESSAGE$29
29693
+ message: MESSAGE$28
30355
29694
  });
30356
29695
  }
30357
29696
  })
@@ -31104,7 +30443,7 @@ const getEnclosingEffectHookCallback = (node, componentFunction) => {
31104
30443
  const isEffectDrivenResync = (useStateCall) => {
31105
30444
  const setterName = getStateSetterName(useStateCall);
31106
30445
  if (!setterName) return false;
31107
- const componentFunction = findEnclosingFunction$1(useStateCall);
30446
+ const componentFunction = findEnclosingFunction(useStateCall);
31108
30447
  if (!componentFunction) return false;
31109
30448
  let isExempt = false;
31110
30449
  walkAst(componentFunction, (child) => {
@@ -31208,7 +30547,7 @@ const isDraftReseedOrRenderAdjusted = (useStateCall, isPropName) => {
31208
30547
  const setterName = getStateSetterName(useStateCall);
31209
30548
  if (!setterName) return false;
31210
30549
  const stateValueName = getStateValueName(useStateCall);
31211
- const componentFunction = findEnclosingFunction$1(useStateCall);
30550
+ const componentFunction = findEnclosingFunction(useStateCall);
31212
30551
  if (!componentFunction) return false;
31213
30552
  let isExempt = false;
31214
30553
  walkAst(componentFunction, (child) => {
@@ -31254,7 +30593,7 @@ const noDerivedUseState = defineRule({
31254
30593
  if (isDraftReseedOrRenderAdjusted(node, propStackTracker.isPropName)) return;
31255
30594
  if (isEffectDrivenResync(node)) return;
31256
30595
  if (isNextjsDataFetchingPage(node)) return;
31257
- const componentFunction = findEnclosingFunction$1(node);
30596
+ const componentFunction = findEnclosingFunction(node);
31258
30597
  if (componentFunction) {
31259
30598
  if (isDefaultedDestructuredProp(componentFunction, propName)) return;
31260
30599
  if (isDraftCommittedToParent(componentFunction, getStateValueName(node), propStackTracker.isPropName)) return;
@@ -31316,7 +30655,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
31316
30655
  //#endregion
31317
30656
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
31318
30657
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
31319
- const MESSAGE$28 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
30658
+ const MESSAGE$27 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
31320
30659
  const getNodeStart = (node) => "start" in node && typeof node.start === "number" ? node.start : -1;
31321
30660
  const getEnclosingLifecycleFunction = (setStateCall) => {
31322
30661
  let ancestor = setStateCall.parent;
@@ -31444,7 +30783,7 @@ const noDidMountSetState = defineRule({
31444
30783
  }
31445
30784
  context.report({
31446
30785
  node: node.callee,
31447
- message: MESSAGE$28
30786
+ message: MESSAGE$27
31448
30787
  });
31449
30788
  } };
31450
30789
  }
@@ -31452,7 +30791,7 @@ const noDidMountSetState = defineRule({
31452
30791
  //#endregion
31453
30792
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
31454
30793
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
31455
- const MESSAGE$27 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
30794
+ const MESSAGE$26 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
31456
30795
  const EQUALITY_OPERATORS = new Set([
31457
30796
  "==",
31458
30797
  "===",
@@ -31626,7 +30965,7 @@ const noDidUpdateSetState = defineRule({
31626
30965
  if (isInsideDiffGuard(node)) return;
31627
30966
  context.report({
31628
30967
  node: node.callee,
31629
- message: MESSAGE$27
30968
+ message: MESSAGE$26
31630
30969
  });
31631
30970
  } };
31632
30971
  }
@@ -31649,7 +30988,7 @@ const isStateMemberExpression = (node) => {
31649
30988
  };
31650
30989
  //#endregion
31651
30990
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
31652
- const MESSAGE$26 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
30991
+ 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.";
31653
30992
  const shouldIgnoreMutation = (node) => {
31654
30993
  let isConstructor = false;
31655
30994
  let isInsideCallExpression = false;
@@ -31671,7 +31010,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
31671
31010
  if (shouldIgnoreMutation(reportNode)) return;
31672
31011
  context.report({
31673
31012
  node: reportNode,
31674
- message: MESSAGE$26
31013
+ message: MESSAGE$25
31675
31014
  });
31676
31015
  };
31677
31016
  const noDirectMutationState = defineRule({
@@ -32022,7 +31361,7 @@ const noDocumentStartViewTransition = defineRule({
32022
31361
  });
32023
31362
  //#endregion
32024
31363
  //#region src/plugin/rules/js-performance/no-document-write.ts
32025
- const MESSAGE$25 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
31364
+ 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.";
32026
31365
  const WRITE_METHODS = new Set(["write", "writeln"]);
32027
31366
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
32028
31367
  const noDocumentWrite = defineRule({
@@ -32039,7 +31378,7 @@ const noDocumentWrite = defineRule({
32039
31378
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
32040
31379
  context.report({
32041
31380
  node,
32042
- message: MESSAGE$25
31381
+ message: MESSAGE$24
32043
31382
  });
32044
31383
  } })
32045
31384
  });
@@ -33092,7 +32431,7 @@ const isFunctionUsedOutsideHandlers = (analysis, functionNode, componentFunction
33092
32431
  if (isInsideInlineEventHandler(identifier, componentFunction)) return false;
33093
32432
  const parent = identifier.parent;
33094
32433
  if (parent && isNodeOfType(parent, "CallExpression") && parent.callee === identifier) {
33095
- if (findEnclosingFunction$1(parent) === functionNode) return false;
32434
+ if (findEnclosingFunction(parent) === functionNode) return false;
33096
32435
  if (isInsideProvenEventHandler(analysis, parent, componentFunction, false)) return false;
33097
32436
  }
33098
32437
  return true;
@@ -33138,7 +32477,7 @@ const isStateWrittenOnlyFromEventHandlers = (analysis, stateReference) => {
33138
32477
  if (!setterVariable) return false;
33139
32478
  const stateDeclarator = getUseStateDecl(analysis, stateReference);
33140
32479
  if (!stateDeclarator) return false;
33141
- const componentFunction = findEnclosingFunction$1(stateDeclarator);
32480
+ const componentFunction = findEnclosingFunction(stateDeclarator);
33142
32481
  if (!componentFunction) return false;
33143
32482
  let hasWriter = false;
33144
32483
  for (const reference of setterVariable.references) {
@@ -34200,8 +33539,8 @@ const isAwaitedInFunction = (request, functionNode) => {
34200
33539
  return false;
34201
33540
  };
34202
33541
  const isCompletionSinkForRequest = (completionSink, request) => {
34203
- const requestFunction = findEnclosingFunction$1(request);
34204
- const completionSinkFunction = findEnclosingFunction$1(completionSink);
33542
+ const requestFunction = findEnclosingFunction(request);
33543
+ const completionSinkFunction = findEnclosingFunction(completionSink);
34205
33544
  if (!requestFunction || !completionSinkFunction) return false;
34206
33545
  if (requestFunction === completionSinkFunction) {
34207
33546
  if (!isAwaitedInFunction(request, requestFunction)) return false;
@@ -34257,7 +33596,7 @@ const ALLOWED_NAMESPACES = new Set([
34257
33596
  "ReactDOM",
34258
33597
  "ReactDom"
34259
33598
  ]);
34260
- const MESSAGE$24 = "`findDOMNode` crashes your app in React 19 because it was removed.";
33599
+ const MESSAGE$23 = "`findDOMNode` crashes your app in React 19 because it was removed.";
34261
33600
  const noFindDomNode = defineRule({
34262
33601
  id: "no-find-dom-node",
34263
33602
  title: "findDOMNode breaks component encapsulation",
@@ -34268,7 +33607,7 @@ const noFindDomNode = defineRule({
34268
33607
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
34269
33608
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
34270
33609
  node: callee,
34271
- message: MESSAGE$24
33610
+ message: MESSAGE$23
34272
33611
  });
34273
33612
  return;
34274
33613
  }
@@ -34279,7 +33618,7 @@ const noFindDomNode = defineRule({
34279
33618
  if (callee.property.name !== "findDOMNode") return;
34280
33619
  context.report({
34281
33620
  node: callee.property,
34282
- message: MESSAGE$24
33621
+ message: MESSAGE$23
34283
33622
  });
34284
33623
  }
34285
33624
  } })
@@ -34488,6 +33827,13 @@ const isPublishedLibraryPackage = (filename) => {
34488
33827
  return manifest.private !== true && typeof manifest.peerDependencies === "object" && manifest.peerDependencies !== null && "react" in manifest.peerDependencies;
34489
33828
  };
34490
33829
  //#endregion
33830
+ //#region src/plugin/utils/is-type-only-import.ts
33831
+ const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
33832
+ if (!specifiers || specifiers.length === 0) return false;
33833
+ return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
33834
+ };
33835
+ const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
33836
+ //#endregion
34491
33837
  //#region src/plugin/rules/bundle-size/no-full-lodash-import.ts
34492
33838
  const isLibraryDevPage = (filename) => {
34493
33839
  if (!filename) return false;
@@ -35051,7 +34397,7 @@ const isInRenderedOutput = (node, componentOrHookNode, scopes) => {
35051
34397
  return attribute ? !isEventHandlerAttribute(attribute) : true;
35052
34398
  }
35053
34399
  if (isNodeOfType(parentNode, "ReturnStatement")) {
35054
- if (findEnclosingFunction$1(parentNode) === componentOrHookNode) return true;
34400
+ if (findEnclosingFunction(parentNode) === componentOrHookNode) return true;
35055
34401
  }
35056
34402
  if (parentNode === componentOrHookNode) return isFunctionLike$2(componentOrHookNode) && !isNodeOfType(componentOrHookNode.body, "BlockStatement") && componentOrHookNode.body === currentNode;
35057
34403
  if (isFunctionLike$2(parentNode) && !executesDuringRender(parentNode, scopes)) return false;
@@ -35174,7 +34520,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
35174
34520
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
35175
34521
  const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
35176
34522
  if (!componentOrHookNode) return;
35177
- const enclosingFunction = findEnclosingFunction$1(node);
34523
+ const enclosingFunction = findEnclosingFunction(node);
35178
34524
  if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
35179
34525
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
35180
34526
  if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
@@ -35186,7 +34532,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
35186
34532
  });
35187
34533
  //#endregion
35188
34534
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
35189
- const MESSAGE$23 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
34535
+ 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.";
35190
34536
  const noImgLazyWithHighFetchpriority = defineRule({
35191
34537
  id: "no-img-lazy-with-high-fetchpriority",
35192
34538
  title: "Lazy image with high fetchPriority",
@@ -35200,7 +34546,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
35200
34546
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
35201
34547
  context.report({
35202
34548
  node: node.name,
35203
- message: MESSAGE$23
34549
+ message: MESSAGE$22
35204
34550
  });
35205
34551
  } })
35206
34552
  });
@@ -35389,7 +34735,7 @@ const noImpureStateUpdater = defineRule({
35389
34735
  });
35390
34736
  //#endregion
35391
34737
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
35392
- const MESSAGE$22 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
34738
+ const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
35393
34739
  const REACT_USE_REF_OPTIONS = {
35394
34740
  allowGlobalReactNamespace: false,
35395
34741
  allowUnboundBareCalls: false
@@ -35490,7 +34836,7 @@ const noIndeterminateAttribute = defineRule({
35490
34836
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
35491
34837
  context.report({
35492
34838
  node: indeterminateAttribute,
35493
- message: MESSAGE$22
34839
+ message: MESSAGE$21
35494
34840
  });
35495
34841
  },
35496
34842
  CallExpression(node) {
@@ -35499,7 +34845,7 @@ const noIndeterminateAttribute = defineRule({
35499
34845
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
35500
34846
  context.report({
35501
34847
  node,
35502
- message: MESSAGE$22
34848
+ message: MESSAGE$21
35503
34849
  });
35504
34850
  }
35505
34851
  };
@@ -35622,10 +34968,10 @@ const isInsideInstanceField = (node) => {
35622
34968
  return false;
35623
34969
  };
35624
34970
  const isOneShotModuleInitialization = (node, context) => {
35625
- let functionNode = findEnclosingFunction$1(node);
34971
+ let functionNode = findEnclosingFunction(node);
35626
34972
  while (functionNode) {
35627
34973
  if (!executesDuringRender(functionNode, context.scopes)) return false;
35628
- functionNode = findEnclosingFunction$1(functionNode);
34974
+ functionNode = findEnclosingFunction(functionNode);
35629
34975
  }
35630
34976
  return !isInsideInstanceField(node);
35631
34977
  };
@@ -35787,7 +35133,7 @@ const noIsMounted = defineRule({
35787
35133
  });
35788
35134
  //#endregion
35789
35135
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
35790
- const MESSAGE$21 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
35136
+ 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)`.";
35791
35137
  const isJsonMethodCall = (node, method) => {
35792
35138
  if (!isNodeOfType(node, "CallExpression")) return false;
35793
35139
  const callee = node.callee;
@@ -35851,13 +35197,13 @@ const noJsonParseStringifyClone = defineRule({
35851
35197
  if (isCatchParameterRoundTrip(firstArgument)) return;
35852
35198
  context.report({
35853
35199
  node,
35854
- message: MESSAGE$21
35200
+ message: MESSAGE$20
35855
35201
  });
35856
35202
  } })
35857
35203
  });
35858
35204
  //#endregion
35859
35205
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
35860
- const MESSAGE$20 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35206
+ const MESSAGE$19 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
35861
35207
  const isJsxElementTypeReference = (node) => {
35862
35208
  if (!isNodeOfType(node, "TSTypeReference")) return false;
35863
35209
  const typeName = node.typeName;
@@ -35911,7 +35257,7 @@ const noJsxElementType = defineRule({
35911
35257
  if (isJsxImported) return;
35912
35258
  for (const typeAnnotation of flaggedAnnotations) context.report({
35913
35259
  node: typeAnnotation,
35914
- message: MESSAGE$20
35260
+ message: MESSAGE$19
35915
35261
  });
35916
35262
  }
35917
35263
  };
@@ -36143,221 +35489,6 @@ const noLegacyClassLifecycles = defineRule({
36143
35489
  }
36144
35490
  });
36145
35491
  //#endregion
36146
- //#region src/plugin/utils/is-proven-react-class-component.ts
36147
- const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
36148
- const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
36149
- const expression = stripParenExpression(node);
36150
- if (isNodeOfType(expression, "MemberExpression")) {
36151
- const propertyName = getStaticPropertyName(expression);
36152
- const receiver = stripParenExpression(expression.object);
36153
- return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
36154
- }
36155
- if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
36156
- if (!isNodeOfType(expression, "Identifier")) return false;
36157
- const symbol = scopes.symbolFor(expression);
36158
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
36159
- visitedSymbolIds.add(symbol.id);
36160
- if (isImportedFromReact(symbol)) {
36161
- const importedName = getImportedName(symbol.declarationNode);
36162
- return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
36163
- }
36164
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
36165
- return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
36166
- };
36167
- const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36168
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
36169
- visitedClassNodes.add(classNode);
36170
- return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
36171
- };
36172
- //#endregion
36173
- //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
36174
- const functionContainsProvenReactHookCall = (functionNode, scopes) => {
36175
- if (!isFunctionLike$2(functionNode)) return false;
36176
- let containsReactHookCall = false;
36177
- walkAst(functionNode.body, (node) => {
36178
- if (containsReactHookCall) return false;
36179
- if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
36180
- if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
36181
- containsReactHookCall = true;
36182
- return false;
36183
- }
36184
- });
36185
- return containsReactHookCall;
36186
- };
36187
- //#endregion
36188
- //#region src/plugin/utils/function-returns-props-children.ts
36189
- const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
36190
- if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
36191
- const firstParameter = stripParenExpression(functionNode.params[0]);
36192
- const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
36193
- const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
36194
- const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
36195
- if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
36196
- for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
36197
- const propertyValue = stripParenExpression(property.value);
36198
- const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
36199
- if (!isNodeOfType(childrenBinding, "Identifier")) continue;
36200
- const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
36201
- if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
36202
- }
36203
- }
36204
- return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
36205
- const candidate = stripParenExpression(expression);
36206
- if (isNodeOfType(candidate, "Identifier")) {
36207
- const symbol = scopes.symbolFor(candidate);
36208
- return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
36209
- }
36210
- if (!isNodeOfType(candidate, "MemberExpression")) return false;
36211
- if (getStaticPropertyName(candidate) !== "children") return false;
36212
- const receiver = stripParenExpression(candidate.object);
36213
- if (!isNodeOfType(receiver, "Identifier")) return false;
36214
- const receiverSymbol = scopes.symbolFor(receiver);
36215
- if (!receiverSymbol || !propsParameterSymbol) return false;
36216
- return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
36217
- }, controlFlow);
36218
- };
36219
- //#endregion
36220
- //#region src/plugin/utils/function-returns-only-null.ts
36221
- const isNullExpression = (expression) => {
36222
- const candidate = stripParenExpression(expression);
36223
- return isNodeOfType(candidate, "Literal") && candidate.value === null;
36224
- };
36225
- const functionReturnsOnlyNull = (functionNode) => {
36226
- if (!isFunctionLike$2(functionNode)) return false;
36227
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
36228
- const returnStatements = collectFunctionReturnStatements(functionNode);
36229
- return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
36230
- };
36231
- //#endregion
36232
- //#region src/plugin/utils/is-proven-styled-component-expression.ts
36233
- const findFactoryRoot = (node) => {
36234
- const candidate = stripParenExpression(node);
36235
- if (isNodeOfType(candidate, "Identifier")) return candidate;
36236
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
36237
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
36238
- return null;
36239
- };
36240
- const findFactoryPropertyName = (node) => {
36241
- const candidate = stripParenExpression(node);
36242
- if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
36243
- if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
36244
- return null;
36245
- };
36246
- const isProvenStyledComponentExpression = (expression, scopes) => {
36247
- const candidate = stripParenExpression(expression);
36248
- if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
36249
- const factoryRoot = findFactoryRoot(candidate.tag);
36250
- if (!factoryRoot) return false;
36251
- const factoryPropertyName = findFactoryPropertyName(candidate.tag);
36252
- if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
36253
- const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
36254
- if (!symbol || symbol.kind !== "import") return false;
36255
- if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
36256
- const importDeclaration = symbol.declarationNode.parent;
36257
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
36258
- };
36259
- //#endregion
36260
- //#region src/plugin/utils/is-proven-react-component-symbol.ts
36261
- const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
36262
- const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
36263
- const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
36264
- const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36265
- const candidate = stripParenExpression(expression);
36266
- if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
36267
- if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
36268
- if (isProvenStyledComponentExpression(candidate, scopes)) return true;
36269
- if (isNodeOfType(candidate, "Identifier")) {
36270
- const symbol = scopes.symbolFor(candidate);
36271
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
36272
- visitedSymbolIds.add(symbol.id);
36273
- if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
36274
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
36275
- return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
36276
- }
36277
- if (!isNodeOfType(candidate, "CallExpression")) return false;
36278
- if (!hasStableCallTarget(candidate, scopes)) return false;
36279
- const factoryCallee = stripParenExpression(candidate.callee);
36280
- if (isNodeOfType(factoryCallee, "Identifier") && isDefaultImportFromModule(factoryCallee, factoryCallee.name, "create-react-class") || isReactApiCall(candidate, LEGACY_REACT_COMPONENT_FACTORY_NAMES, scopes)) return true;
36281
- if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
36282
- const wrappedComponent = candidate.arguments[0];
36283
- return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
36284
- }
36285
- if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
36286
- const factory = candidate.arguments[0];
36287
- if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
36288
- const unwrappedFactory = stripParenExpression(factory);
36289
- if (!isInlineFunctionExpression(unwrappedFactory)) return false;
36290
- if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
36291
- const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
36292
- const returnedExpression = returnStatements[0]?.argument;
36293
- return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
36294
- };
36295
- const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
36296
- const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
36297
- for (const candidateSymbol of candidateSymbols) {
36298
- if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
36299
- if (isComponentDeclaration(candidateSymbol.declarationNode)) {
36300
- if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
36301
- continue;
36302
- }
36303
- const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
36304
- if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
36305
- if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
36306
- continue;
36307
- }
36308
- if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
36309
- if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
36310
- continue;
36311
- }
36312
- if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
36313
- }
36314
- return false;
36315
- };
36316
- //#endregion
36317
- //#region src/plugin/utils/symbol-has-react-component-type-annotation.ts
36318
- const REACT_COMPONENT_TYPE_NAMES = new Set([
36319
- "ComponentClass",
36320
- "ComponentType",
36321
- "FC",
36322
- "FunctionComponent"
36323
- ]);
36324
- const findVisibleSymbol = (identifier, scopes) => {
36325
- if (!isNodeOfType(identifier, "Identifier")) return null;
36326
- let scope = scopes.scopeFor(identifier);
36327
- while (true) {
36328
- const symbol = scope.symbolsByName.get(identifier.name);
36329
- if (symbol) return symbol;
36330
- if (!scope.parent) return null;
36331
- scope = scope.parent;
36332
- }
36333
- };
36334
- const isReactNamespaceType = (identifier, scopes) => {
36335
- const symbol = findVisibleSymbol(identifier, scopes);
36336
- return Boolean(symbol && isImportedFromReact(symbol) && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
36337
- };
36338
- const isReactComponentType = (typeNode, scopes, visitedSymbolIds) => {
36339
- if (!typeNode) return false;
36340
- if (isNodeOfType(typeNode, "TSTypeAnnotation")) return isReactComponentType(typeNode.typeAnnotation, scopes, visitedSymbolIds);
36341
- if (isNodeOfType(typeNode, "TSIntersectionType")) return (typeNode.types ?? []).some((member) => isReactComponentType(member, scopes, visitedSymbolIds));
36342
- if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
36343
- const typeName = typeNode.typeName;
36344
- if (isNodeOfType(typeName, "TSQualifiedName")) return isNodeOfType(typeName.left, "Identifier") && isNodeOfType(typeName.right, "Identifier") && REACT_COMPONENT_TYPE_NAMES.has(typeName.right.name) && isReactNamespaceType(typeName.left, scopes);
36345
- if (!isNodeOfType(typeName, "Identifier")) return false;
36346
- const typeSymbol = findVisibleSymbol(typeName, scopes);
36347
- if (!typeSymbol || visitedSymbolIds.has(typeSymbol.id)) return false;
36348
- if (isImportedFromReact(typeSymbol)) {
36349
- const importedName = getImportedName(typeSymbol.declarationNode);
36350
- return Boolean(importedName && REACT_COMPONENT_TYPE_NAMES.has(importedName));
36351
- }
36352
- if (!isNodeOfType(typeSymbol.declarationNode, "TSTypeAliasDeclaration")) return false;
36353
- visitedSymbolIds.add(typeSymbol.id);
36354
- return isReactComponentType(typeSymbol.declarationNode.typeAnnotation, scopes, visitedSymbolIds);
36355
- };
36356
- const symbolHasReactComponentTypeAnnotation = (symbol, scopes) => {
36357
- const bindingIdentifier = symbol.bindingIdentifier;
36358
- return isNodeOfType(bindingIdentifier, "Identifier") && isReactComponentType(bindingIdentifier.typeAnnotation, scopes, /* @__PURE__ */ new Set());
36359
- };
36360
- //#endregion
36361
35492
  //#region src/plugin/rules/architecture/no-legacy-context-api.ts
36362
35493
  const LEGACY_CONTEXT_NAMES = new Set([
36363
35494
  "childContextTypes",
@@ -36368,6 +35499,15 @@ const buildLegacyContextMessage = (memberName) => {
36368
35499
  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.`;
36369
35500
  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.";
36370
35501
  };
35502
+ const isInsideClassBody = (node) => {
35503
+ let current = node.parent;
35504
+ while (current) {
35505
+ if (isNodeOfType(current, "ClassBody")) return true;
35506
+ if (isFunctionLike$2(current)) return false;
35507
+ current = current.parent;
35508
+ }
35509
+ return false;
35510
+ };
36371
35511
  const noLegacyContextApi = defineRule({
36372
35512
  id: "no-legacy-context-api",
36373
35513
  title: "Legacy context API",
@@ -36382,7 +35522,6 @@ const noLegacyContextApi = defineRule({
36382
35522
  if (!isNodeOfType(memberNode, "MethodDefinition") && !isNodeOfType(memberNode, "PropertyDefinition")) return;
36383
35523
  if (!isNodeOfType(memberNode.key, "Identifier")) return;
36384
35524
  if (!LEGACY_CONTEXT_NAMES.has(memberNode.key.name)) return;
36385
- if (memberNode.key.name === "getChildContext" ? memberNode.static : !memberNode.static) return;
36386
35525
  context.report({
36387
35526
  node: memberNode.key,
36388
35527
  message: buildLegacyContextMessage(memberNode.key.name)
@@ -36390,8 +35529,6 @@ const noLegacyContextApi = defineRule({
36390
35529
  };
36391
35530
  return {
36392
35531
  ClassBody(node) {
36393
- const classNode = node.parent;
36394
- if (!classNode || !isProvenReactClassComponent(classNode, context.scopes)) return;
36395
35532
  for (const member of node.body ?? []) checkMember(member);
36396
35533
  },
36397
35534
  AssignmentExpression(node) {
@@ -36401,11 +35538,9 @@ const noLegacyContextApi = defineRule({
36401
35538
  if (left.computed) return;
36402
35539
  if (!isNodeOfType(left.property, "Identifier")) return;
36403
35540
  if (!LEGACY_CONTEXT_NAMES.has(left.property.name)) return;
36404
- if (left.property.name === "getChildContext") return;
36405
- const component = stripParenExpression(left.object);
36406
- if (!isNodeOfType(component, "Identifier")) return;
36407
- const symbol = context.scopes.symbolFor(component);
36408
- if (!symbol || !isProvenReactComponentSymbol(symbol, context.scopes, context.cfg, component) && (hasSymbolWriteBefore(symbol, component, context.scopes) || !symbolHasReactComponentTypeAnnotation(symbol, context.scopes))) return;
35541
+ if (!isNodeOfType(left.object, "Identifier")) return;
35542
+ if (!isUppercaseName(left.object.name)) return;
35543
+ if (isInsideClassBody(node)) return;
36409
35544
  context.report({
36410
35545
  node: left,
36411
35546
  message: buildLegacyContextMessage(left.property.name)
@@ -36609,10 +35744,10 @@ const getMethodCalls = (functionNode, scopes) => {
36609
35744
  const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, scopes, visitedSymbolIds, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
36610
35745
  if (visitedFunctionNodes.has(functionNode)) return false;
36611
35746
  visitedFunctionNodes.add(functionNode);
36612
- const usageFunction = findEnclosingFunction$1(usageNode);
35747
+ const usageFunction = findEnclosingFunction(usageNode);
36613
35748
  const immediateCall = getDirectCallForExpression(functionNode);
36614
35749
  if (immediateCall) {
36615
- const immediateCallFunction = findEnclosingFunction$1(immediateCall);
35750
+ const immediateCallFunction = findEnclosingFunction(immediateCall);
36616
35751
  if (immediateCallFunction === usageFunction) {
36617
35752
  const immediateCallStart = getRangeStart(immediateCall);
36618
35753
  return immediateCallStart === null || immediateCallStart < usageBoundary;
@@ -36621,7 +35756,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
36621
35756
  return isFunctionInvokedBeforeUsage(immediateCallFunction, usageNode, usageBoundary, scopes, visitedSymbolIds, new Set(visitedFunctionNodes));
36622
35757
  }
36623
35758
  for (const methodCall of getMethodCalls(functionNode, scopes)) {
36624
- const methodCallFunction = findEnclosingFunction$1(methodCall);
35759
+ const methodCallFunction = findEnclosingFunction(methodCall);
36625
35760
  if (methodCallFunction === usageFunction) {
36626
35761
  const methodCallStart = getRangeStart(methodCall);
36627
35762
  if (methodCallStart === null || methodCallStart < usageBoundary) return true;
@@ -36644,7 +35779,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
36644
35779
  if (scopes.symbolFor(child)?.declarationNode !== symbol.declarationNode) return;
36645
35780
  const call = getDirectCallForExpression(child);
36646
35781
  if (!call) return false;
36647
- const callFunction = findEnclosingFunction$1(call);
35782
+ const callFunction = findEnclosingFunction(call);
36648
35783
  if (callFunction === usageFunction) {
36649
35784
  const callStart = getRangeStart(call);
36650
35785
  wasInvokedBeforeUsage = callStart === null || callStart < usageBoundary;
@@ -36675,14 +35810,14 @@ const wasMutatedBeforeUsage = (symbol, usageNode, readNode, scopes, visitedMutat
36675
35810
  visitedMutationSymbolIds.add(symbol.id);
36676
35811
  const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, readNode) : inheritedUsageBoundary;
36677
35812
  if (typeof usageBoundary !== "number") return true;
36678
- const usageFunction = findEnclosingFunction$1(usageNode);
35813
+ const usageFunction = findEnclosingFunction(usageNode);
36679
35814
  return symbol.references.some((reference) => {
36680
35815
  const referenceStart = getRangeStart(reference.identifier);
36681
35816
  const simpleAlias = getSimpleAlias(reference.identifier, scopes);
36682
35817
  if (simpleAlias) return wasMutatedBeforeUsage(simpleAlias.symbol, usageNode, readNodesBySymbolId.get(simpleAlias.symbol.id) ?? simpleAlias.readNode, scopes, new Set(visitedMutationSymbolIds), usageBoundary, readNodesBySymbolId);
36683
35818
  if (!isPotentialMutationReference(reference.identifier, readNode)) return false;
36684
35819
  if (referenceStart === null) return true;
36685
- const mutationFunction = findEnclosingFunction$1(reference.identifier);
35820
+ const mutationFunction = findEnclosingFunction(reference.identifier);
36686
35821
  if (mutationFunction === usageFunction) return referenceStart < usageBoundary;
36687
35822
  if (!mutationFunction) return usageFunction !== null;
36688
35823
  if (usageFunction && isAstDescendant(usageFunction, mutationFunction)) return true;
@@ -37227,7 +36362,7 @@ const noMoment = defineRule({
37227
36362
  });
37228
36363
  //#endregion
37229
36364
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
37230
- const MESSAGE$19 = "This file declares several components, so each component is harder to find, test, and change.";
36365
+ const MESSAGE$18 = "This file declares several components, so each component is harder to find, test, and change.";
37231
36366
  const resolveSettings$16 = (settings) => {
37232
36367
  const reactDoctor = settings?.["react-doctor"];
37233
36368
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -37264,6 +36399,13 @@ const isReactNamespaceExpression = (node, scopes) => {
37264
36399
  return isReactNamespaceImport(node, scopes);
37265
36400
  };
37266
36401
  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));
36402
+ const getDestructuredPropertyName$1 = (symbol) => {
36403
+ const property = symbol.bindingIdentifier.parent;
36404
+ if (!property || !isNodeOfType(property, "Property") || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
36405
+ if (isNodeOfType(property.key, "Identifier") && !property.computed) return property.key.name;
36406
+ if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
36407
+ return null;
36408
+ };
37267
36409
  const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
37268
36410
  if (visitedSymbolIds.has(symbol.id)) return false;
37269
36411
  visitedSymbolIds.add(symbol.id);
@@ -37273,7 +36415,7 @@ const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
37273
36415
  }
37274
36416
  const init = symbol.initializer;
37275
36417
  if (!init) return false;
37276
- const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
36418
+ const destructuredPropertyName = getDestructuredPropertyName$1(symbol);
37277
36419
  if (destructuredPropertyName && REACT_HOC_NAMES.has(destructuredPropertyName) && isReactNamespaceExpression(init, scopes)) return true;
37278
36420
  if (isReactHocMemberReference(init, scopes)) return true;
37279
36421
  if (isNodeOfType(init, "Identifier")) {
@@ -37579,7 +36721,7 @@ const noMultiComp = defineRule({
37579
36721
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
37580
36722
  for (const component of flagged.slice(1)) context.report({
37581
36723
  node: component.reportNode,
37582
- message: MESSAGE$19
36724
+ message: MESSAGE$18
37583
36725
  });
37584
36726
  } };
37585
36727
  }
@@ -37792,7 +36934,7 @@ const resolveReducerFunction = (node, currentFilename) => {
37792
36934
  };
37793
36935
  //#endregion
37794
36936
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
37795
- const MESSAGE$18 = "This reducer changes state in place, so your update is silently skipped.";
36937
+ const MESSAGE$17 = "This reducer changes state in place, so your update is silently skipped.";
37796
36938
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
37797
36939
  "copyWithin",
37798
36940
  "fill",
@@ -38001,7 +37143,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
38001
37143
  reportedNodes.add(options.crossFileConsumerCallSite);
38002
37144
  context.report({
38003
37145
  node: options.crossFileConsumerCallSite,
38004
- message: `${MESSAGE$18} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
37146
+ message: `${MESSAGE$17} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
38005
37147
  });
38006
37148
  return;
38007
37149
  }
@@ -38010,7 +37152,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
38010
37152
  reportedNodes.add(mutation.node);
38011
37153
  context.report({
38012
37154
  node: mutation.node,
38013
- message: MESSAGE$18
37155
+ message: MESSAGE$17
38014
37156
  });
38015
37157
  }
38016
37158
  };
@@ -38417,7 +37559,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
38417
37559
  });
38418
37560
  //#endregion
38419
37561
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
38420
- const MESSAGE$17 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
37562
+ const MESSAGE$16 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
38421
37563
  const KEYBOARD_HANDLER_PROP_NAMES = [
38422
37564
  "onKeyDown",
38423
37565
  "onKeyUp",
@@ -38536,7 +37678,7 @@ const noNoninteractiveTabindex = defineRule({
38536
37678
  if (numeric === null) {
38537
37679
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
38538
37680
  node: tabIndex,
38539
- message: MESSAGE$17
37681
+ message: MESSAGE$16
38540
37682
  });
38541
37683
  return;
38542
37684
  }
@@ -38558,7 +37700,7 @@ const noNoninteractiveTabindex = defineRule({
38558
37700
  if (!roleAttribute) {
38559
37701
  context.report({
38560
37702
  node: tabIndex,
38561
- message: MESSAGE$17
37703
+ message: MESSAGE$16
38562
37704
  });
38563
37705
  return;
38564
37706
  }
@@ -38572,7 +37714,7 @@ const noNoninteractiveTabindex = defineRule({
38572
37714
  }
38573
37715
  context.report({
38574
37716
  node: tabIndex,
38575
- message: MESSAGE$17
37717
+ message: MESSAGE$16
38576
37718
  });
38577
37719
  } };
38578
37720
  }
@@ -38999,6 +38141,8 @@ const getWrapperHookWrappedFunction = (initializer) => {
38999
38141
  if (!wrapped || !isFunctionLike$2(wrapped)) return null;
39000
38142
  return wrapped;
39001
38143
  };
38144
+ const hasParameterDef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "Parameter"));
38145
+ const resolvesToFunctionBinding = (ref) => !hasParameterDef(ref) && Boolean(resolveToFunction(ref));
39002
38146
  const HANDLER_NAMED_PROP_PATTERN = /^(on|handle)[A-Z]/;
39003
38147
  const wrappedFunctionNotifiesParent = (analysis, wrappedFunction) => getDownstreamRefs(analysis, wrappedFunction).some((innerRef) => {
39004
38148
  if (!isProp(analysis, innerRef)) return false;
@@ -39027,6 +38171,12 @@ const getDeclarationKind = (declarator) => {
39027
38171
  return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
39028
38172
  };
39029
38173
  const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
38174
+ const getDestructuredPropertyName = (bindingIdentifier) => {
38175
+ const property = bindingIdentifier.parent;
38176
+ if (!property || !isNodeOfType(property, "Property")) return null;
38177
+ if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
38178
+ return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
38179
+ };
39030
38180
  const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
39031
38181
  const unwrappedExpression = stripParenExpression(expression);
39032
38182
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -39036,7 +38186,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
39036
38186
  if (isProp(analysis, callbackReference)) {
39037
38187
  if (isWholePropsObjectReference(analysis, callbackReference)) return null;
39038
38188
  const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
39039
- return (bindingIdentifier && getDestructuredBindingPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
38189
+ return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
39040
38190
  }
39041
38191
  if (hasMutableBindingWrite(callbackReference)) return null;
39042
38192
  const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
@@ -39046,7 +38196,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
39046
38196
  visitedVariables.add(callbackVariable);
39047
38197
  if (isNodeOfType(declarator.id, "ObjectPattern")) {
39048
38198
  const bindingIdentifier = callbackVariable.defs[0]?.name;
39049
- const propertyName = bindingIdentifier ? getDestructuredBindingPropertyName(bindingIdentifier) : null;
38199
+ const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
39050
38200
  const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
39051
38201
  return propertyName && propsReference ? propertyName : null;
39052
38202
  }
@@ -39149,7 +38299,7 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
39149
38299
  const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
39150
38300
  if (!bindingProvenance) return null;
39151
38301
  const { refCall, variables } = bindingProvenance;
39152
- const componentFunction = findEnclosingFunction$1(effectCall);
38302
+ const componentFunction = findEnclosingFunction(effectCall);
39153
38303
  if (!componentFunction || !isFunctionLike$2(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
39154
38304
  if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
39155
38305
  const callbackPropNames = /* @__PURE__ */ new Set();
@@ -39313,7 +38463,7 @@ const noPassDataToParent = defineRule({
39313
38463
  if (isConstant(argRef)) return false;
39314
38464
  if (isParentWiredHookResultRef(analysis, argRef)) return false;
39315
38465
  if (isParentWiredHookCalleeRef(analysis, argRef)) return false;
39316
- if (resolveToFunction(argRef)) return false;
38466
+ if (resolvesToFunctionBinding(argRef)) return false;
39317
38467
  const argIdentifier = argRef.identifier;
39318
38468
  if (isImportBindingRef(argRef) && !isCalleePosition(argIdentifier)) return false;
39319
38469
  if (isNodeOfType(argIdentifier, "Identifier") && argIdentifier.name === "undefined") return false;
@@ -39985,6 +39135,174 @@ const noPropCallbackInRender = defineRule({
39985
39135
  } })
39986
39136
  });
39987
39137
  //#endregion
39138
+ //#region src/plugin/utils/is-proven-react-class-component.ts
39139
+ const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
39140
+ const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
39141
+ const expression = stripParenExpression(node);
39142
+ if (isNodeOfType(expression, "MemberExpression")) {
39143
+ const propertyName = getStaticPropertyName(expression);
39144
+ const receiver = stripParenExpression(expression.object);
39145
+ return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
39146
+ }
39147
+ if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
39148
+ if (!isNodeOfType(expression, "Identifier")) return false;
39149
+ const symbol = scopes.symbolFor(expression);
39150
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
39151
+ visitedSymbolIds.add(symbol.id);
39152
+ if (isImportedFromReact(symbol)) {
39153
+ const importedName = getImportedName(symbol.declarationNode);
39154
+ return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
39155
+ }
39156
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
39157
+ return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
39158
+ };
39159
+ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39160
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
39161
+ visitedClassNodes.add(classNode);
39162
+ return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
39163
+ };
39164
+ //#endregion
39165
+ //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
39166
+ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
39167
+ if (!isFunctionLike$2(functionNode)) return false;
39168
+ let containsReactHookCall = false;
39169
+ walkAst(functionNode.body, (node) => {
39170
+ if (containsReactHookCall) return false;
39171
+ if (node !== functionNode.body && (isFunctionLike$2(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
39172
+ if (isNodeOfType(node, "CallExpression") && isReactApiCall(node, BUILTIN_HOOK_NAMES, scopes, { resolveNamedAliases: true })) {
39173
+ containsReactHookCall = true;
39174
+ return false;
39175
+ }
39176
+ });
39177
+ return containsReactHookCall;
39178
+ };
39179
+ //#endregion
39180
+ //#region src/plugin/utils/function-returns-props-children.ts
39181
+ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
39182
+ if (!isFunctionLike$2(functionNode) || functionNode.params.length === 0) return false;
39183
+ const firstParameter = stripParenExpression(functionNode.params[0]);
39184
+ const firstParameterPattern = isNodeOfType(firstParameter, "AssignmentPattern") ? stripParenExpression(firstParameter.left) : firstParameter;
39185
+ const propsParameterSymbol = isNodeOfType(firstParameterPattern, "Identifier") ? scopes.symbolFor(firstParameterPattern) : null;
39186
+ const childrenBindingSymbolIds = /* @__PURE__ */ new Set();
39187
+ if (isNodeOfType(firstParameterPattern, "ObjectPattern")) {
39188
+ for (const property of firstParameterPattern.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children") {
39189
+ const propertyValue = stripParenExpression(property.value);
39190
+ const childrenBinding = isNodeOfType(propertyValue, "AssignmentPattern") ? stripParenExpression(propertyValue.left) : propertyValue;
39191
+ if (!isNodeOfType(childrenBinding, "Identifier")) continue;
39192
+ const childrenBindingSymbol = scopes.symbolFor(childrenBinding);
39193
+ if (childrenBindingSymbol) childrenBindingSymbolIds.add(childrenBindingSymbol.id);
39194
+ }
39195
+ }
39196
+ return functionReturnsMatchingExpression(functionNode, scopes, (expression) => {
39197
+ const candidate = stripParenExpression(expression);
39198
+ if (isNodeOfType(candidate, "Identifier")) {
39199
+ const symbol = scopes.symbolFor(candidate);
39200
+ return Boolean(symbol && childrenBindingSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, candidate, scopes));
39201
+ }
39202
+ if (!isNodeOfType(candidate, "MemberExpression")) return false;
39203
+ if (getStaticPropertyName(candidate) !== "children") return false;
39204
+ const receiver = stripParenExpression(candidate.object);
39205
+ if (!isNodeOfType(receiver, "Identifier")) return false;
39206
+ const receiverSymbol = scopes.symbolFor(receiver);
39207
+ if (!receiverSymbol || !propsParameterSymbol) return false;
39208
+ return receiverSymbol.id === propsParameterSymbol.id && !hasSymbolWriteBefore(receiverSymbol, candidate, scopes) && !hasStaticPropertyWriteBefore(receiver, "children", candidate, scopes);
39209
+ }, controlFlow);
39210
+ };
39211
+ //#endregion
39212
+ //#region src/plugin/utils/function-returns-only-null.ts
39213
+ const isNullExpression = (expression) => {
39214
+ const candidate = stripParenExpression(expression);
39215
+ return isNodeOfType(candidate, "Literal") && candidate.value === null;
39216
+ };
39217
+ const functionReturnsOnlyNull = (functionNode) => {
39218
+ if (!isFunctionLike$2(functionNode)) return false;
39219
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
39220
+ const returnStatements = collectFunctionReturnStatements(functionNode);
39221
+ return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
39222
+ };
39223
+ //#endregion
39224
+ //#region src/plugin/utils/is-proven-styled-component-expression.ts
39225
+ const findFactoryRoot = (node) => {
39226
+ const candidate = stripParenExpression(node);
39227
+ if (isNodeOfType(candidate, "Identifier")) return candidate;
39228
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryRoot(candidate.object);
39229
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryRoot(candidate.callee);
39230
+ return null;
39231
+ };
39232
+ const findFactoryPropertyName = (node) => {
39233
+ const candidate = stripParenExpression(node);
39234
+ if (isNodeOfType(candidate, "MemberExpression")) return findFactoryPropertyName(candidate.object) ?? getStaticPropertyName(candidate);
39235
+ if (isNodeOfType(candidate, "CallExpression")) return findFactoryPropertyName(candidate.callee);
39236
+ return null;
39237
+ };
39238
+ const isProvenStyledComponentExpression = (expression, scopes) => {
39239
+ const candidate = stripParenExpression(expression);
39240
+ if (!isNodeOfType(candidate, "TaggedTemplateExpression")) return false;
39241
+ const factoryRoot = findFactoryRoot(candidate.tag);
39242
+ if (!factoryRoot) return false;
39243
+ const factoryPropertyName = findFactoryPropertyName(candidate.tag);
39244
+ if (factoryPropertyName && hasStaticPropertyWriteBefore(factoryRoot, factoryPropertyName, candidate, scopes)) return false;
39245
+ const symbol = resolveConstIdentifierAlias(factoryRoot, scopes);
39246
+ if (!symbol || symbol.kind !== "import") return false;
39247
+ if (!(isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "styled")) return false;
39248
+ const importDeclaration = symbol.declarationNode.parent;
39249
+ return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "styled-components");
39250
+ };
39251
+ //#endregion
39252
+ //#region src/plugin/utils/is-proven-react-component-symbol.ts
39253
+ const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
39254
+ const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39255
+ const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39256
+ const candidate = stripParenExpression(expression);
39257
+ if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
39258
+ if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
39259
+ if (isProvenStyledComponentExpression(candidate, scopes)) return true;
39260
+ if (isNodeOfType(candidate, "Identifier")) {
39261
+ const symbol = scopes.symbolFor(candidate);
39262
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
39263
+ visitedSymbolIds.add(symbol.id);
39264
+ if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
39265
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
39266
+ return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
39267
+ }
39268
+ if (!isNodeOfType(candidate, "CallExpression")) return false;
39269
+ if (!hasStableCallTarget(candidate, scopes)) return false;
39270
+ if (isReactApiCall(candidate, REACT_COMPONENT_HOC_NAMES, scopes, { resolveNamedAliases: true })) {
39271
+ const wrappedComponent = candidate.arguments[0];
39272
+ return Boolean(wrappedComponent && !isNodeOfType(wrappedComponent, "SpreadElement") && isProvenReactComponentExpression(wrappedComponent, scopes, controlFlow, visitedSymbolIds));
39273
+ }
39274
+ if (!isReactApiCall(candidate, "useMemo", scopes, { resolveNamedAliases: true })) return false;
39275
+ const factory = candidate.arguments[0];
39276
+ if (!factory || isNodeOfType(factory, "SpreadElement")) return false;
39277
+ const unwrappedFactory = stripParenExpression(factory);
39278
+ if (!isInlineFunctionExpression(unwrappedFactory)) return false;
39279
+ if (!isNodeOfType(unwrappedFactory.body, "BlockStatement")) return isProvenReactComponentExpression(unwrappedFactory.body, scopes, controlFlow, visitedSymbolIds);
39280
+ const returnStatements = collectFunctionReturnStatements(unwrappedFactory);
39281
+ const returnedExpression = returnStatements[0]?.argument;
39282
+ return Boolean(returnStatements.length === 1 && returnedExpression && isProvenReactComponentExpression(returnedExpression, scopes, controlFlow, visitedSymbolIds));
39283
+ };
39284
+ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentReference) => {
39285
+ const candidateSymbols = symbol.kind === "ts-module" ? symbol.scope.symbols.filter((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.kind !== "ts-module") : [symbol];
39286
+ for (const candidateSymbol of candidateSymbols) {
39287
+ if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
39288
+ if (isComponentDeclaration(candidateSymbol.declarationNode)) {
39289
+ if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
39290
+ continue;
39291
+ }
39292
+ const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
39293
+ if (isNodeOfType(candidateSymbol.declarationNode, "VariableDeclarator") && isNodeOfType(candidateSymbol.declarationNode.id, "Identifier") && isUppercaseName(candidateSymbol.declarationNode.id.name) && initializer) {
39294
+ if (isProvenReactComponentExpression(initializer, scopes, controlFlow)) return true;
39295
+ continue;
39296
+ }
39297
+ if (isNodeOfType(candidateSymbol.declarationNode, "ClassDeclaration") || isNodeOfType(candidateSymbol.declarationNode, "ClassExpression")) {
39298
+ if (isProvenReactClassComponent(candidateSymbol.declarationNode, scopes)) return true;
39299
+ continue;
39300
+ }
39301
+ if (initializer && isNodeOfType(initializer, "ClassExpression") && isProvenReactClassComponent(initializer, scopes)) return true;
39302
+ }
39303
+ return false;
39304
+ };
39305
+ //#endregion
39988
39306
  //#region src/plugin/rules/architecture/no-prop-types.ts
39989
39307
  const PROP_TYPES_PROPERTY = "propTypes";
39990
39308
  const isPropTypesKey = (key, computed) => {
@@ -40137,7 +39455,7 @@ const isModuleScopedBinding = (identifier) => {
40137
39455
  const binding = findVariableInitializer(identifier, identifier.name);
40138
39456
  if (!binding) return false;
40139
39457
  if (isNodeOfType(binding.scopeOwner, "Program")) return true;
40140
- if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction$1(binding.scopeOwner) === null;
39458
+ if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction(binding.scopeOwner) === null;
40141
39459
  return false;
40142
39460
  };
40143
39461
  const looksLikeFreshUpdateExpression = (expression) => {
@@ -40192,7 +39510,7 @@ const noRandomKey = defineRule({
40192
39510
  });
40193
39511
  //#endregion
40194
39512
  //#region src/plugin/rules/react-builtins/no-react-children.ts
40195
- const MESSAGE$16 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
39513
+ const MESSAGE$15 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
40196
39514
  const isChildrenIdentifier = (node, contextNode) => {
40197
39515
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
40198
39516
  return isImportedFromModule(contextNode, "Children", "react");
@@ -40218,13 +39536,13 @@ const noReactChildren = defineRule({
40218
39536
  if (isChildrenIdentifier(memberObject, node)) {
40219
39537
  context.report({
40220
39538
  node: calleeOuter,
40221
- message: MESSAGE$16
39539
+ message: MESSAGE$15
40222
39540
  });
40223
39541
  return;
40224
39542
  }
40225
39543
  if (isReactNamespaceMember(memberObject, node)) context.report({
40226
39544
  node: calleeOuter,
40227
- message: MESSAGE$16
39545
+ message: MESSAGE$15
40228
39546
  });
40229
39547
  } })
40230
39548
  });
@@ -40905,7 +40223,7 @@ const noRenderPropChildren = defineRule({
40905
40223
  });
40906
40224
  //#endregion
40907
40225
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
40908
- const MESSAGE$15 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40226
+ const MESSAGE$14 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
40909
40227
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
40910
40228
  const isUsedAsReturnValue = (parent) => {
40911
40229
  if (!parent) return false;
@@ -40923,7 +40241,7 @@ const noRenderReturnValue = defineRule({
40923
40241
  if (!isUsedAsReturnValue(node.parent)) return;
40924
40242
  context.report({
40925
40243
  node: node.callee,
40926
- message: MESSAGE$15
40244
+ message: MESSAGE$14
40927
40245
  });
40928
40246
  } })
40929
40247
  });
@@ -41732,7 +41050,7 @@ const noSelfUpdatingEffect = defineRule({
41732
41050
  });
41733
41051
  //#endregion
41734
41052
  //#region src/plugin/rules/react-builtins/no-set-state.ts
41735
- const MESSAGE$14 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41053
+ const MESSAGE$13 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
41736
41054
  const noSetState = defineRule({
41737
41055
  id: "no-set-state",
41738
41056
  title: "Local class state forbidden",
@@ -41747,7 +41065,7 @@ const noSetState = defineRule({
41747
41065
  if (!getParentComponent(node)) return;
41748
41066
  context.report({
41749
41067
  node: node.callee,
41750
- message: MESSAGE$14
41068
+ message: MESSAGE$13
41751
41069
  });
41752
41070
  } })
41753
41071
  });
@@ -42054,9 +41372,9 @@ const isReturnedFromAnyEffectInScope = (functionNode, ownerScope) => {
42054
41372
  return isReturnedFromEffect;
42055
41373
  };
42056
41374
  const isInsideEffectCleanupReturn = (node, ownerScope) => {
42057
- let functionNode = findEnclosingFunction$1(node);
41375
+ let functionNode = findEnclosingFunction(node);
42058
41376
  while (functionNode) {
42059
- const outerFunction = findEnclosingFunction$1(functionNode);
41377
+ const outerFunction = findEnclosingFunction(functionNode);
42060
41378
  if (outerFunction && isEffectCallbackFunction(outerFunction) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
42061
41379
  if (isReturnedFromAnyEffectInScope(functionNode, ownerScope)) return true;
42062
41380
  functionNode = outerFunction;
@@ -42064,7 +41382,7 @@ const isInsideEffectCleanupReturn = (node, ownerScope) => {
42064
41382
  return false;
42065
41383
  };
42066
41384
  const hasRefCurrentReassignmentAfterClear = (clearCall, refName) => {
42067
- const enclosingFunction = findEnclosingFunction$1(clearCall);
41385
+ const enclosingFunction = findEnclosingFunction(clearCall);
42068
41386
  if (!isFunctionLike$2(enclosingFunction)) return false;
42069
41387
  if (!isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
42070
41388
  const clearStart = getRangeStart(clearCall);
@@ -42117,7 +41435,7 @@ const isAbstractRole = (openingElement, settings) => {
42117
41435
  };
42118
41436
  //#endregion
42119
41437
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
42120
- const MESSAGE$13 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
41438
+ 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.";
42121
41439
  const DEFAULT_HANDLERS = [
42122
41440
  "onClick",
42123
41441
  "onMouseDown",
@@ -42232,7 +41550,7 @@ const noStaticElementInteractions = defineRule({
42232
41550
  if (!roleAttribute || !roleAttribute.value) {
42233
41551
  context.report({
42234
41552
  node: node.name,
42235
- message: MESSAGE$13
41553
+ message: MESSAGE$12
42236
41554
  });
42237
41555
  return;
42238
41556
  }
@@ -42242,7 +41560,7 @@ const noStaticElementInteractions = defineRule({
42242
41560
  if (isRecognizedRoleString(attributeValue.value)) return;
42243
41561
  context.report({
42244
41562
  node: node.name,
42245
- message: MESSAGE$13
41563
+ message: MESSAGE$12
42246
41564
  });
42247
41565
  return;
42248
41566
  }
@@ -42250,7 +41568,7 @@ const noStaticElementInteractions = defineRule({
42250
41568
  if (!settings.allowExpressionValues) {
42251
41569
  context.report({
42252
41570
  node: node.name,
42253
- message: MESSAGE$13
41571
+ message: MESSAGE$12
42254
41572
  });
42255
41573
  return;
42256
41574
  }
@@ -42258,7 +41576,7 @@ const noStaticElementInteractions = defineRule({
42258
41576
  if (isStaticNullishExpression(expression)) {
42259
41577
  context.report({
42260
41578
  node: node.name,
42261
- message: MESSAGE$13
41579
+ message: MESSAGE$12
42262
41580
  });
42263
41581
  return;
42264
41582
  }
@@ -42268,7 +41586,7 @@ const noStaticElementInteractions = defineRule({
42268
41586
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
42269
41587
  context.report({
42270
41588
  node: node.name,
42271
- message: MESSAGE$13
41589
+ message: MESSAGE$12
42272
41590
  });
42273
41591
  return;
42274
41592
  }
@@ -42276,7 +41594,7 @@ const noStaticElementInteractions = defineRule({
42276
41594
  }
42277
41595
  context.report({
42278
41596
  node: node.name,
42279
- message: MESSAGE$13
41597
+ message: MESSAGE$12
42280
41598
  });
42281
41599
  } };
42282
41600
  }
@@ -42382,7 +41700,7 @@ const noStringRefs = defineRule({
42382
41700
  });
42383
41701
  //#endregion
42384
41702
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
42385
- const MESSAGE$12 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
41703
+ 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)`).";
42386
41704
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
42387
41705
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
42388
41706
  const noSyncXhr = defineRule({
@@ -42403,7 +41721,7 @@ const noSyncXhr = defineRule({
42403
41721
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
42404
41722
  context.report({
42405
41723
  node,
42406
- message: MESSAGE$12
41724
+ message: MESSAGE$11
42407
41725
  });
42408
41726
  };
42409
41727
  return {
@@ -42428,7 +41746,7 @@ const noSyncXhr = defineRule({
42428
41746
  });
42429
41747
  //#endregion
42430
41748
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
42431
- const MESSAGE$11 = "This value is `undefined` because function components have no `this`.";
41749
+ const MESSAGE$10 = "This value is `undefined` because function components have no `this`.";
42432
41750
  const isInsideClassMethod = (node, customClassFactoryNames) => {
42433
41751
  let ancestor = node.parent;
42434
41752
  while (ancestor) {
@@ -42516,7 +41834,7 @@ const noThisInSfc = defineRule({
42516
41834
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
42517
41835
  context.report({
42518
41836
  node,
42519
- message: MESSAGE$11
41837
+ message: MESSAGE$10
42520
41838
  });
42521
41839
  } };
42522
41840
  }
@@ -42894,7 +42212,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
42894
42212
  return false;
42895
42213
  };
42896
42214
  const isAfterAvailabilityEarlyExit = (node, componentOrHookNode, browserGlobalName, context) => {
42897
- const enclosingFunction = findEnclosingFunction$1(node);
42215
+ const enclosingFunction = findEnclosingFunction(node);
42898
42216
  if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$2(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
42899
42217
  let currentNode = node;
42900
42218
  while (currentNode !== enclosingFunction) {
@@ -44161,11 +43479,7 @@ const resolveSettings$8 = (settings) => {
44161
43479
  propNamePattern: ruleSettings.propNamePattern ?? "render*"
44162
43480
  };
44163
43481
  };
44164
- const isReactCreateElementCall = (node, scopes) => isReactApiCall(node, "createElement", scopes, {
44165
- allowGlobalReactNamespace: true,
44166
- resolveNamedAliases: true
44167
- });
44168
- const expressionContainsJsxOrCreateElement = (root, scopes) => {
43482
+ const expressionContainsJsxOrCreateElement = (root) => {
44169
43483
  let found = false;
44170
43484
  walkAst(root, (node) => {
44171
43485
  if (found) return false;
@@ -44174,17 +43488,17 @@ const expressionContainsJsxOrCreateElement = (root, scopes) => {
44174
43488
  found = true;
44175
43489
  return false;
44176
43490
  }
44177
- if (isNodeOfType(node, "CallExpression") && isReactCreateElementCall(node, scopes)) {
43491
+ if (isNodeOfType(node, "CallExpression") && isCreateElementCall(node)) {
44178
43492
  found = true;
44179
43493
  return false;
44180
43494
  }
44181
43495
  });
44182
43496
  return found;
44183
43497
  };
44184
- const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => expressionContainsJsxOrCreateElement(expression, scopes), controlFlow);
44185
- const isReactClassComponent = (classNode, scopes) => {
43498
+ const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement, controlFlow);
43499
+ const isReactClassComponent = (classNode) => {
44186
43500
  if (isEs6Component(classNode)) return true;
44187
- return expressionContainsJsxOrCreateElement(classNode, scopes);
43501
+ return expressionContainsJsxOrCreateElement(classNode);
44188
43502
  };
44189
43503
  const findEnclosingComponent = (node, scopes, controlFlow) => {
44190
43504
  let walker = node.parent;
@@ -44201,7 +43515,7 @@ const findEnclosingComponent = (node, scopes, controlFlow) => {
44201
43515
  };
44202
43516
  }
44203
43517
  if (isNodeOfType(walker, "ClassDeclaration") || isNodeOfType(walker, "ClassExpression")) {
44204
- if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker, scopes)) return {
43518
+ if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker)) return {
44205
43519
  component: walker,
44206
43520
  name: walker.id.name
44207
43521
  };
@@ -44290,12 +43604,12 @@ const isObjectCallbackCandidate = (node) => {
44290
43604
  }
44291
43605
  return false;
44292
43606
  };
44293
- const hocCallContainsComponent = (call, scopes) => {
43607
+ const hocCallContainsComponent = (call) => {
44294
43608
  const firstArgument = call.arguments[0];
44295
43609
  if (!firstArgument) return false;
44296
- if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument, scopes);
44297
- if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument, scopes);
44298
- return expressionContainsJsxOrCreateElement(firstArgument, scopes);
43610
+ if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument);
43611
+ if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument);
43612
+ return expressionContainsJsxOrCreateElement(firstArgument);
44299
43613
  };
44300
43614
  const isFirstArgumentOfHocCall = (node) => {
44301
43615
  const parent = node.parent;
@@ -44328,35 +43642,19 @@ const isReturnOfMapCallback = (node) => {
44328
43642
  }
44329
43643
  return false;
44330
43644
  };
43645
+ const isCloneElementCall = (node) => {
43646
+ if (!isNodeOfType(node, "CallExpression")) return false;
43647
+ const callee = node.callee;
43648
+ if (isNodeOfType(callee, "Identifier")) return callee.name === "cloneElement";
43649
+ return isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "cloneElement";
43650
+ };
44331
43651
  const TS_VALUE_PASSTHROUGH_TYPES = new Set([
44332
43652
  "TSAsExpression",
44333
43653
  "TSNonNullExpression",
44334
43654
  "TSSatisfiesExpression",
44335
43655
  "TSTypeAssertion"
44336
43656
  ]);
44337
- const ELEMENT_TYPE_PROP_NAMES = new Set([
44338
- "as",
44339
- "body",
44340
- "calendarcontainer",
44341
- "component",
44342
- "fallback",
44343
- "tooltip"
44344
- ]);
44345
- const isElementTypeJsxAttribute = (node) => {
44346
- if (!isNodeOfType(node, "JSXAttribute")) return false;
44347
- if (!isNodeOfType(node.name, "JSXIdentifier")) return false;
44348
- const attributeName = node.name.name;
44349
- return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
44350
- };
44351
- const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
44352
- allowGlobalReactNamespace: true,
44353
- resolveNamedAliases: true
44354
- });
44355
- const isReactLazyCall = (call, scopes) => isReactApiCall(call, "lazy", scopes, {
44356
- allowGlobalReactNamespace: true,
44357
- resolveNamedAliases: true
44358
- });
44359
- const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
43657
+ const isRenderFlowingReadReference = (identifier) => {
44360
43658
  let valueNode = identifier;
44361
43659
  let parent = valueNode.parent;
44362
43660
  while (parent) {
@@ -44366,26 +43664,20 @@ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @_
44366
43664
  continue;
44367
43665
  }
44368
43666
  switch (parent.type) {
44369
- case "JSXOpeningElement": return parent.name === valueNode;
44370
- case "JSXExpressionContainer": return Boolean(parent.parent && isElementTypeJsxAttribute(parent.parent));
44371
- case "ReturnStatement": return false;
44372
- case "ArrowFunctionExpression": return false;
43667
+ case "JSXExpressionContainer":
43668
+ case "ReturnStatement": return true;
43669
+ case "ArrowFunctionExpression": return parent.body === valueNode;
44373
43670
  case "CallExpression":
44374
43671
  if (parent.callee === valueNode) return false;
44375
- if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
44376
- if (isReactCreateElementCall(parent, scopes)) return true;
43672
+ if (isCreateElementCall(parent) || isCloneElementCall(parent)) return true;
44377
43673
  valueNode = parent;
44378
43674
  parent = parent.parent;
44379
43675
  continue;
44380
- case "VariableDeclarator": {
44381
- if (parent.init !== valueNode || !isNodeOfType(parent.id, "Identifier")) return false;
44382
- const aliasSymbol = scopes.symbolFor(parent.id);
44383
- if (!aliasSymbol || aliasSymbol.kind !== "const" || visitedSymbols.has(aliasSymbol.id)) return false;
44384
- const nextVisitedSymbols = new Set(visitedSymbols);
44385
- nextVisitedSymbols.add(aliasSymbol.id);
44386
- return aliasSymbol.references.some((reference) => reference.flag === "read" && isRenderFlowingReadReference(reference.identifier, scopes, nextVisitedSymbols));
43676
+ case "VariableDeclarator": return parent.init === valueNode && isNodeOfType(parent.id, "Identifier") && isReactComponentName(parent.id.name);
43677
+ case "AssignmentExpression": {
43678
+ const assignmentTarget = parent.left;
43679
+ return parent.right === valueNode && isNodeOfType(assignmentTarget, "Identifier") && isReactComponentName(assignmentTarget.name);
44387
43680
  }
44388
- case "AssignmentExpression": return false;
44389
43681
  case "Property":
44390
43682
  if (parent.value !== valueNode) return false;
44391
43683
  valueNode = parent;
@@ -44423,7 +43715,6 @@ const noUnstableNestedComponents = defineRule({
44423
43715
  severity: "warn",
44424
43716
  recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
44425
43717
  category: "Performance",
44426
- tags: ["react-jsx-only"],
44427
43718
  create: (context) => {
44428
43719
  const settings = resolveSettings$8(context.settings);
44429
43720
  const renderPropRegex = compileGlob(settings.propNamePattern);
@@ -44486,7 +43777,7 @@ const noUnstableNestedComponents = defineRule({
44486
43777
  },
44487
43778
  Identifier(node) {
44488
43779
  if (!isReactComponentName(node.name)) return;
44489
- if (!isRenderFlowingReadReference(node, context.scopes)) return;
43780
+ if (!isRenderFlowingReadReference(node)) return;
44490
43781
  recordInstantiation(node, node.name);
44491
43782
  },
44492
43783
  FunctionDeclaration: checkFunctionLike,
@@ -44495,33 +43786,28 @@ const noUnstableNestedComponents = defineRule({
44495
43786
  ClassDeclaration(node) {
44496
43787
  if (!node.id) return;
44497
43788
  if (!isReactComponentName(node.id.name)) return;
44498
- if (!isReactClassComponent(node, context.scopes)) return;
43789
+ if (!isReactClassComponent(node)) return;
44499
43790
  enqueueCandidate(node, null);
44500
43791
  },
44501
43792
  ClassExpression(node) {
44502
43793
  const inferredName = node.id?.name ?? inferFunctionLikeName(node);
44503
43794
  if (!inferredName || !isReactComponentName(inferredName)) return;
44504
- if (!isReactClassComponent(node, context.scopes)) return;
43795
+ if (!isReactClassComponent(node)) return;
44505
43796
  enqueueCandidate(node, null);
44506
43797
  },
44507
43798
  CallExpression(node) {
44508
- if (isReactCreateElementCall(node, context.scopes)) {
43799
+ if (isCreateElementCall(node)) {
44509
43800
  const firstArgument = node.arguments[0];
44510
43801
  if (firstArgument && isNodeOfType(firstArgument, "Identifier")) recordInstantiation(firstArgument, firstArgument.name);
44511
43802
  else if (firstArgument && isNodeOfType(firstArgument, "MemberExpression")) recordMemberChainInstantiation(firstArgument);
44512
43803
  }
44513
- const isReactLazy = isReactLazyCall(node, context.scopes);
44514
- if (!isReactLazy && !isHocCallee$1(node)) return;
44515
- if (!isReactLazy && !hocCallContainsComponent(node, context.scopes)) return;
44516
- const inferredName = inferFunctionLikeName(node);
44517
- const propInfo = isComponentDeclaredInProp(node);
44518
- if (propInfo === null && (!inferredName || !isReactComponentName(inferredName))) return;
44519
- enqueueCandidate(node, propInfo === null ? inferredName : null);
43804
+ if (!isHocCallee$1(node)) return;
43805
+ if (!hocCallContainsComponent(node)) return;
43806
+ enqueueCandidate(node, null);
44520
43807
  },
44521
43808
  "Program:exit"() {
44522
43809
  for (const report of queuedReports) {
44523
43810
  if (report.requiredInstantiationName !== null) {
44524
- if ((report.requiredInstantiationBinding ? context.scopes.symbolFor(report.requiredInstantiationBinding) : null)?.references.some((reference) => reference.flag === "write" || reference.flag === "read-write")) continue;
44525
43811
  if (!(report.requiredInstantiationBinding !== null ? instantiatedBindingIdentifiers.has(report.requiredInstantiationBinding) : instantiatedComponentNames.has(report.requiredInstantiationName))) continue;
44526
43812
  }
44527
43813
  context.report({
@@ -44695,7 +43981,7 @@ const noWideLetterSpacing = defineRule({
44695
43981
  //#endregion
44696
43982
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
44697
43983
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
44698
- const MESSAGE$10 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
43984
+ const MESSAGE$9 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
44699
43985
  const resolveSettings$7 = (settings) => {
44700
43986
  const reactDoctor = settings?.["react-doctor"];
44701
43987
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -44729,7 +44015,7 @@ const noWillUpdateSetState = defineRule({
44729
44015
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
44730
44016
  context.report({
44731
44017
  node: node.callee,
44732
- message: MESSAGE$10
44018
+ message: MESSAGE$9
44733
44019
  });
44734
44020
  } };
44735
44021
  }
@@ -45554,7 +44840,7 @@ const findChildrenDestructuringFunction = (identifier, scopes) => {
45554
44840
  const symbol = scopes.symbolFor(identifier);
45555
44841
  if (symbol) {
45556
44842
  if (symbol.kind !== "parameter") return null;
45557
- const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
44843
+ const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45558
44844
  if (declaringFunction && destructuresChildrenAsFirstParam(declaringFunction)) return declaringFunction;
45559
44845
  return null;
45560
44846
  }
@@ -45600,7 +44886,7 @@ const isChildrenMemberExpression = (node, scopes) => {
45600
44886
  const symbol = scopes.symbolFor(propsObject);
45601
44887
  if (symbol) {
45602
44888
  if (symbol.kind !== "parameter") return false;
45603
- const declaringFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
44889
+ const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
45604
44890
  return declaringFunction ? isDeclaringFunctionComponentLike(declaringFunction) : false;
45605
44891
  }
45606
44892
  return hasComponentLikeAncestorFunction(node);
@@ -45725,7 +45011,7 @@ const preactNoRenderArguments = defineRule({
45725
45011
  });
45726
45012
  //#endregion
45727
45013
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
45728
- const MESSAGE$9 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
45014
+ 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.";
45729
45015
  const preactPreferOndblclick = defineRule({
45730
45016
  id: "preact-prefer-ondblclick",
45731
45017
  title: "onDoubleClick instead of onDblClick",
@@ -45740,7 +45026,7 @@ const preactPreferOndblclick = defineRule({
45740
45026
  if (!onDoubleClickAttribute) return;
45741
45027
  context.report({
45742
45028
  node: onDoubleClickAttribute,
45743
- message: MESSAGE$9
45029
+ message: MESSAGE$8
45744
45030
  });
45745
45031
  } })
45746
45032
  });
@@ -45991,7 +45277,7 @@ const preferExplicitVariants = defineRule({
45991
45277
  });
45992
45278
  //#endregion
45993
45279
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
45994
- const MESSAGE$8 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
45280
+ 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.";
45995
45281
  const resolveSettings$4 = (settings) => {
45996
45282
  const reactDoctor = settings?.["react-doctor"];
45997
45283
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -46030,7 +45316,7 @@ const preferFunctionComponent = defineRule({
46030
45316
  const reportNode = node.id ?? node;
46031
45317
  context.report({
46032
45318
  node: reportNode,
46033
- message: MESSAGE$8
45319
+ message: MESSAGE$7
46034
45320
  });
46035
45321
  };
46036
45322
  return {
@@ -46252,7 +45538,7 @@ const doesFunctionReturnsObjectLiteral = (functionNode) => {
46252
45538
  //#endregion
46253
45539
  //#region src/plugin/utils/enclosing-component-or-hook-scope.ts
46254
45540
  const enclosingComponentOrHookScope = (startNode, ownScopeFor) => {
46255
- const functionNode = findEnclosingFunction$1(startNode);
45541
+ const functionNode = findEnclosingFunction(startNode);
46256
45542
  if (!functionNode) return null;
46257
45543
  const displayName = componentOrHookDisplayNameForFunction(functionNode);
46258
45544
  if (!displayName) return null;
@@ -47314,7 +46600,7 @@ const isTanstackQuerySource = (source) => TANSTACK_QUERY_PACKAGE_PATTERN.test(so
47314
46600
  //#region src/plugin/rules/tanstack-query/query-destructure-result.ts
47315
46601
  const isHookReturnForwardingSpread = (objectExpression) => {
47316
46602
  const objectParent = objectExpression.parent;
47317
- const enclosingFunction = findEnclosingFunction$1(objectExpression);
46603
+ const enclosingFunction = findEnclosingFunction(objectExpression);
47318
46604
  if (!enclosingFunction) return false;
47319
46605
  if (!(isNodeOfType(objectParent, "ReturnStatement") || isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && enclosingFunction.body === objectExpression)) return false;
47320
46606
  const enclosingName = componentOrHookDisplayNameForFunction(enclosingFunction);
@@ -47608,258 +46894,7 @@ const queryMutationMissingInvalidation = defineRule({
47608
46894
  }
47609
46895
  });
47610
46896
  //#endregion
47611
- //#region src/plugin/utils/is-node-conditionally-executed.ts
47612
- const isNodeConditionallyExecuted = (node, boundary) => {
47613
- let child = node;
47614
- let parent = child.parent ?? null;
47615
- while (parent && parent !== boundary) {
47616
- if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === child || parent.alternate === child)) return true;
47617
- if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) return true;
47618
- if (isNodeOfType(parent, "AssignmentPattern") && parent.right === child) return true;
47619
- child = parent;
47620
- parent = child.parent ?? null;
47621
- }
47622
- return false;
47623
- };
47624
- //#endregion
47625
- //#region src/plugin/rules/tanstack-query/utils/resolve-tanstack-query-hook-name.ts
47626
- const resolveTanstackNamespaceHookName = (memberExpression, contextNode, scopes) => {
47627
- const hookName = getStaticPropertyKeyName(memberExpression, { allowComputedString: true });
47628
- const namespaceObject = stripParenExpression(memberExpression.object);
47629
- if (!hookName || !TANSTACK_QUERY_HOOKS.has(hookName)) return null;
47630
- if (!isNodeOfType(namespaceObject, "Identifier")) return null;
47631
- const resolvedNamespaceSymbol = resolveConstIdentifierAlias(namespaceObject, scopes);
47632
- if (resolvedNamespaceSymbol?.kind !== "import") return null;
47633
- const namespaceBinding = getImportBindingForName(contextNode, resolvedNamespaceSymbol.name);
47634
- return namespaceBinding?.isNamespace && isTanstackQuerySource(namespaceBinding.source) ? hookName : null;
47635
- };
47636
- const resolveTanstackQueryHookName = (callExpression, scopes) => {
47637
- const callee = stripParenExpression(callExpression.callee);
47638
- if (isNodeOfType(callee, "Identifier")) {
47639
- const resolvedSymbol = resolveConstIdentifierAlias(callee, scopes);
47640
- if (!resolvedSymbol) return null;
47641
- if (resolvedSymbol.kind === "const" && resolvedSymbol.initializer) {
47642
- const initializer = stripParenExpression(resolvedSymbol.initializer);
47643
- return isNodeOfType(initializer, "MemberExpression") ? resolveTanstackNamespaceHookName(initializer, callExpression, scopes) : null;
47644
- }
47645
- if (resolvedSymbol.kind !== "import") return null;
47646
- const importBinding = getImportBindingForName(callExpression, resolvedSymbol.name);
47647
- if (importBinding === null) return null;
47648
- if (importBinding.isNamespace || !isTanstackQuerySource(importBinding.source)) return null;
47649
- return importBinding.exportedName !== null && TANSTACK_QUERY_HOOKS.has(importBinding.exportedName) ? importBinding.exportedName : null;
47650
- }
47651
- if (isNodeOfType(callee, "MemberExpression")) return resolveTanstackNamespaceHookName(callee, callExpression, scopes);
47652
- return null;
47653
- };
47654
- const resolveTanstackQueryHookNameFromInitializer = (initializer, scopes) => {
47655
- const unwrappedInitializer = stripParenExpression(initializer);
47656
- if (isNodeOfType(unwrappedInitializer, "CallExpression")) return resolveTanstackQueryHookName(unwrappedInitializer, scopes);
47657
- if (!isNodeOfType(unwrappedInitializer, "Identifier")) return null;
47658
- const resolvedSymbol = resolveConstIdentifierAlias(unwrappedInitializer, scopes);
47659
- if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
47660
- const resolvedInitializer = stripParenExpression(resolvedSymbol.initializer);
47661
- if (!isNodeOfType(resolvedInitializer, "CallExpression")) return null;
47662
- return resolveTanstackQueryHookName(resolvedInitializer, scopes);
47663
- };
47664
- //#endregion
47665
46897
  //#region src/plugin/rules/tanstack-query/query-no-query-in-effect.ts
47666
- const isTanstackQueryResult = (expression, context) => Boolean(resolveTanstackQueryHookNameFromInitializer(expression, context.scopes));
47667
- const isStaticRefetchMember = (memberExpression) => getStaticPropertyKeyName(memberExpression, { allowComputedString: true }) === "refetch";
47668
- const resolveCalledFunction = (callee, context) => {
47669
- const unwrappedCallee = stripParenExpression(callee);
47670
- if (isFunctionLike$2(unwrappedCallee)) return unwrappedCallee;
47671
- if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
47672
- const symbol = resolveConstIdentifierAlias(unwrappedCallee, context.scopes);
47673
- if (!symbol) return null;
47674
- const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
47675
- return candidate && isFunctionLike$2(candidate) ? candidate : null;
47676
- };
47677
- const hasSuspensionBefore = (functionNode, boundary, context) => {
47678
- if (!isFunctionLike$2(functionNode)) return true;
47679
- if (functionNode.generator) return true;
47680
- const boundaryStart = getRangeStart(boundary);
47681
- if (boundaryStart === null) return true;
47682
- let hasSuspension = false;
47683
- walkAst(functionNode, (node) => {
47684
- if (node !== functionNode && isFunctionLike$2(node)) return false;
47685
- if (!isNodeOfType(node, "AwaitExpression") || !isNodeReachableWithinFunction(node, context)) return;
47686
- const suspensionStart = getRangeStart(node);
47687
- if (suspensionStart !== null && suspensionStart < boundaryStart) {
47688
- hasSuspension = true;
47689
- return false;
47690
- }
47691
- });
47692
- return hasSuspension;
47693
- };
47694
- const isFunctionAncestor = (ancestor, functionNode) => {
47695
- let enclosingFunction = findEnclosingFunction$1(functionNode);
47696
- while (enclosingFunction) {
47697
- if (enclosingFunction === ancestor) return true;
47698
- enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47699
- }
47700
- return false;
47701
- };
47702
- const isUnconditionallyExecuted = (node, functionNode, context) => context.cfg.isUnconditionalFromEntry(node) && !isNodeConditionallyExecuted(node, functionNode) && !(isNodeOfType(node, "MemberExpression") && isNodeOfType(node.parent, "AssignmentExpression") && node.parent.left === node && (node.parent.operator === "&&=" || node.parent.operator === "||=" || node.parent.operator === "??="));
47703
- const functionInvokesTarget = (callerFunction, targetFunction, context, visitedFunctions, canCrossSuspension = false) => {
47704
- if (visitedFunctions.has(callerFunction)) return false;
47705
- visitedFunctions.add(callerFunction);
47706
- let invokesTarget = false;
47707
- walkAst(callerFunction, (node) => {
47708
- if (node !== callerFunction && isFunctionLike$2(node)) return false;
47709
- if (!isNodeOfType(node, "CallExpression")) return;
47710
- if (!isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context) || !canCrossSuspension && hasSuspensionBefore(callerFunction, node, context)) return;
47711
- const calledFunction = resolveCalledFunction(node.callee, context);
47712
- if (calledFunction === targetFunction || calledFunction && functionInvokesTarget(calledFunction, targetFunction, context, visitedFunctions, canCrossSuspension)) {
47713
- invokesTarget = true;
47714
- return false;
47715
- }
47716
- });
47717
- return invokesTarget;
47718
- };
47719
- const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
47720
- const boundaryFunction = findEnclosingFunction$1(boundary);
47721
- const boundaryStart = getRangeStart(boundary);
47722
- if (!boundaryFunction || boundaryStart === null) return false;
47723
- let isInvokedBefore = false;
47724
- walkAst(boundaryFunction, (node) => {
47725
- if (node !== boundaryFunction && isFunctionLike$2(node)) return false;
47726
- if (!isNodeOfType(node, "CallExpression")) return;
47727
- const callStart = getRangeStart(node);
47728
- if (callStart === null || callStart >= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, boundaryFunction, context) || hasSuspensionBefore(boundaryFunction, node, context)) return;
47729
- const calledFunction = resolveCalledFunction(node.callee, context);
47730
- if (calledFunction === invokedFunction || calledFunction && functionInvokesTarget(calledFunction, invokedFunction, context, /* @__PURE__ */ new Set())) {
47731
- isInvokedBefore = true;
47732
- return false;
47733
- }
47734
- });
47735
- return isInvokedBefore;
47736
- };
47737
- const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, context) => {
47738
- const boundaryStart = getRangeStart(boundary);
47739
- if (boundaryStart === null) return false;
47740
- let isInvokedAfter = false;
47741
- walkAst(callerFunction, (node) => {
47742
- if (node !== callerFunction && isFunctionLike$2(node)) return false;
47743
- if (!isNodeOfType(node, "CallExpression")) return;
47744
- const callStart = getRangeStart(node);
47745
- if (callStart === null || callStart <= boundaryStart || !isNodeReachableWithinFunction(node, context) || !isUnconditionallyExecuted(node, callerFunction, context)) return;
47746
- const calledFunction = resolveCalledFunction(node.callee, context);
47747
- if (calledFunction === invokedFunction || calledFunction && functionInvokesTarget(calledFunction, invokedFunction, context, /* @__PURE__ */ new Set(), true)) {
47748
- isInvokedAfter = true;
47749
- return false;
47750
- }
47751
- });
47752
- return isInvokedAfter;
47753
- };
47754
- const isWriteExecutedBefore = (writeNode, boundary, context, deferredExecutionFunction) => {
47755
- const writeStart = getRangeStart(writeNode);
47756
- const boundaryStart = getRangeStart(boundary);
47757
- const writeFunction = findEnclosingFunction$1(writeNode);
47758
- const writeExecutionBoundary = writeFunction ?? findProgramRoot(writeNode);
47759
- if (writeStart === null || boundaryStart === null || !writeExecutionBoundary || !isNodeReachableWithinFunction(writeNode, context) || !isUnconditionallyExecuted(writeNode, writeExecutionBoundary, context)) return false;
47760
- const boundaryFunction = findEnclosingFunction$1(boundary);
47761
- const renderFunction = deferredExecutionFunction ? findEnclosingFunction$1(deferredExecutionFunction) : null;
47762
- if (writeFunction === boundaryFunction) return writeStart < boundaryStart;
47763
- if (!writeFunction) return writeStart < boundaryStart;
47764
- if (boundaryFunction && isFunctionAncestor(writeFunction, boundaryFunction)) {
47765
- if (Boolean(renderFunction && (writeFunction === renderFunction || isFunctionAncestor(writeFunction, renderFunction)))) return true;
47766
- if (deferredExecutionFunction && boundaryFunction !== deferredExecutionFunction) {
47767
- if (hasSuspensionBefore(boundaryFunction, boundary, context) && isFunctionInvokedBefore(boundaryFunction, writeNode, context)) return true;
47768
- return isFunctionInvokedAfter(boundaryFunction, writeNode, writeFunction, context);
47769
- }
47770
- return writeStart < boundaryStart;
47771
- }
47772
- if (renderFunction && isFunctionAncestor(renderFunction, writeFunction) && !hasSuspensionBefore(writeFunction, writeNode, context) && functionInvokesTarget(renderFunction, writeFunction, context, /* @__PURE__ */ new Set())) return true;
47773
- return !hasSuspensionBefore(writeFunction, writeNode, context) && isFunctionInvokedBefore(writeFunction, boundary, context);
47774
- };
47775
- const getStaticStringValue = (node) => {
47776
- const unwrappedNode = stripParenExpression(node);
47777
- if (isNodeOfType(unwrappedNode, "Literal") && typeof unwrappedNode.value === "string") return unwrappedNode.value;
47778
- if (isNodeOfType(unwrappedNode, "TemplateLiteral") && unwrappedNode.expressions.length === 0) return getStaticTemplateLiteralValue(unwrappedNode);
47779
- return null;
47780
- };
47781
- const isSameRefetchMember = (target, candidate, context) => {
47782
- const unwrappedTarget = stripParenExpression(target);
47783
- const unwrappedCandidate = stripParenExpression(candidate);
47784
- if (!isNodeOfType(unwrappedTarget, "Identifier") || !isNodeOfType(unwrappedCandidate, "MemberExpression") || !isStaticRefetchMember(unwrappedCandidate)) return false;
47785
- const candidateTarget = stripParenExpression(unwrappedCandidate.object);
47786
- if (!isNodeOfType(candidateTarget, "Identifier")) return false;
47787
- const targetSymbol = resolveConstIdentifierAlias(unwrappedTarget, context.scopes);
47788
- const candidateSymbol = resolveConstIdentifierAlias(candidateTarget, context.scopes);
47789
- return Boolean(targetSymbol && candidateSymbol?.id === targetSymbol.id);
47790
- };
47791
- const getRefetchMutationTarget = (node, context) => {
47792
- if (isNodeOfType(node, "MemberExpression") && isStaticRefetchMember(node)) {
47793
- const parent = node.parent;
47794
- if (isNodeOfType(parent, "AssignmentExpression") && parent.left === node && isSameRefetchMember(node.object, parent.right, context)) return null;
47795
- return isNodeOfType(parent, "AssignmentExpression") && parent.left === node || isNodeOfType(parent, "UpdateExpression") && parent.argument === node || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" ? node.object : null;
47796
- }
47797
- if (!isNodeOfType(node, "CallExpression")) return null;
47798
- const callee = stripParenExpression(node.callee);
47799
- if (!isNodeOfType(callee, "MemberExpression")) return null;
47800
- const receiver = stripParenExpression(callee.object);
47801
- if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || context.scopes.symbolFor(receiver)) return null;
47802
- const methodName = getStaticPropertyKeyName(callee, { allowComputedString: true });
47803
- const target = node.arguments[0];
47804
- if (!target || isNodeOfType(target, "SpreadElement")) return null;
47805
- if (methodName === "defineProperty") {
47806
- const propertyKey = node.arguments[1];
47807
- if (!propertyKey || getStaticStringValue(propertyKey) !== "refetch") return null;
47808
- const descriptor = node.arguments[2];
47809
- if (isNodeOfType(descriptor, "ObjectExpression")) {
47810
- const valueProperty = descriptor.properties.find((property) => isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "value");
47811
- if (isNodeOfType(valueProperty, "Property") && isSameRefetchMember(target, valueProperty.value, context)) return null;
47812
- }
47813
- return target;
47814
- }
47815
- if (methodName !== "assign") return null;
47816
- let finalRefetchValue = null;
47817
- for (const source of node.arguments.slice(1)) {
47818
- if (!isNodeOfType(source, "ObjectExpression")) continue;
47819
- for (const property of source.properties) if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "refetch") finalRefetchValue = property.value;
47820
- }
47821
- if (!finalRefetchValue || isSameRefetchMember(target, finalRefetchValue, context)) return null;
47822
- return target;
47823
- };
47824
- const hasRefetchMemberWriteBefore = (expression, boundary, context, deferredExecutionFunction) => {
47825
- const unwrappedExpression = stripParenExpression(expression);
47826
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return false;
47827
- const resultSymbol = resolveConstIdentifierAlias(unwrappedExpression, context.scopes);
47828
- if (!resultSymbol) return false;
47829
- const program = findProgramRoot(expression);
47830
- if (!program) return true;
47831
- if (getRangeStart(boundary) === null) return true;
47832
- let hasWrite = false;
47833
- walkAst(program, (node) => {
47834
- const mutationTarget = getRefetchMutationTarget(node, context);
47835
- if (!mutationTarget) return;
47836
- if (!isWriteExecutedBefore(node, boundary, context, deferredExecutionFunction)) return;
47837
- const object = stripParenExpression(mutationTarget);
47838
- if (!isNodeOfType(object, "Identifier")) return;
47839
- if (resolveConstIdentifierAlias(object, context.scopes)?.id === resultSymbol.id) {
47840
- hasWrite = true;
47841
- return false;
47842
- }
47843
- });
47844
- return hasWrite;
47845
- };
47846
- const isTanstackRefetchExpression = (expression, context, deferredExecutionFunction, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
47847
- const unwrappedExpression = stripParenExpression(expression);
47848
- if (isNodeOfType(unwrappedExpression, "MemberExpression")) return isStaticRefetchMember(unwrappedExpression) && isTanstackQueryResult(unwrappedExpression.object, context) && !hasRefetchMemberWriteBefore(unwrappedExpression.object, unwrappedExpression, context, deferredExecutionFunction);
47849
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return false;
47850
- const symbol = context.scopes.symbolFor(unwrappedExpression);
47851
- if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read") || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
47852
- visitedSymbolIds.add(symbol.id);
47853
- const bindingProperty = symbol.bindingIdentifier.parent;
47854
- if (isNodeOfType(bindingProperty, "Property") && getStaticPropertyKeyName(bindingProperty, { allowComputedString: true }) === "refetch") {
47855
- const initializer = symbol.declarationNode.init;
47856
- return Boolean(initializer && isTanstackQueryResult(initializer, context) && !hasRefetchMemberWriteBefore(initializer, symbol.declarationNode, context, null));
47857
- }
47858
- return Boolean(symbol.declarationNode.id === symbol.bindingIdentifier && symbol.initializer && isTanstackRefetchExpression(symbol.initializer, context, null, visitedSymbolIds));
47859
- };
47860
- const isTanstackRefetchCall = (callExpression, context, effectCallback) => {
47861
- return isTanstackRefetchExpression(callExpression.callee, context, effectCallback);
47862
- };
47863
46898
  const queryNoQueryInEffect = defineRule({
47864
46899
  id: "query-no-query-in-effect",
47865
46900
  title: "Query refetch inside useEffect",
@@ -47875,7 +46910,8 @@ const queryNoQueryInEffect = defineRule({
47875
46910
  walkAst(callback, (child) => {
47876
46911
  if (child !== callback && isFunctionLike$2(child) && !effectInvokedFunctions.has(child)) return false;
47877
46912
  if (!isNodeOfType(child, "CallExpression")) return;
47878
- if (isTanstackRefetchCall(child, context, callback)) context.report({
46913
+ const callee = child.callee;
46914
+ if (isNodeOfType(callee, "Identifier") && callee.name === "refetch" || isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === "refetch") context.report({
47879
46915
  node: child,
47880
46916
  message: "refetch() inside useEffect duplicates work React Query already does, causing extra fetches."
47881
46917
  });
@@ -47884,6 +46920,28 @@ const queryNoQueryInEffect = defineRule({
47884
46920
  });
47885
46921
  //#endregion
47886
46922
  //#region src/plugin/rules/tanstack-query/query-no-rest-destructuring.ts
46923
+ const resolveTanstackQueryHookName = (callExpression) => {
46924
+ const callee = callExpression.callee;
46925
+ if (isNodeOfType(callee, "Identifier")) {
46926
+ const importBinding = getImportBindingForName(callExpression, callee.name);
46927
+ if (importBinding === null) return TANSTACK_QUERY_HOOKS.has(callee.name) ? callee.name : null;
46928
+ if (importBinding.isNamespace || !isTanstackQuerySource(importBinding.source)) return null;
46929
+ return importBinding.exportedName !== null && TANSTACK_QUERY_HOOKS.has(importBinding.exportedName) ? importBinding.exportedName : null;
46930
+ }
46931
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && TANSTACK_QUERY_HOOKS.has(callee.property.name) && isNodeOfType(callee.object, "Identifier")) {
46932
+ const namespaceBinding = getImportBindingForName(callExpression, callee.object.name);
46933
+ if (namespaceBinding?.isNamespace && isTanstackQuerySource(namespaceBinding.source)) return callee.property.name;
46934
+ }
46935
+ return null;
46936
+ };
46937
+ const resolveHookNameFromInitializer = (initializer, scopes) => {
46938
+ if (isNodeOfType(initializer, "CallExpression")) return resolveTanstackQueryHookName(initializer);
46939
+ if (!isNodeOfType(initializer, "Identifier")) return null;
46940
+ const resolvedSymbol = resolveConstIdentifierAlias(initializer, scopes);
46941
+ if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
46942
+ if (!isNodeOfType(resolvedSymbol.initializer, "CallExpression")) return null;
46943
+ return resolveTanstackQueryHookName(resolvedSymbol.initializer);
46944
+ };
47887
46945
  const queryNoRestDestructuring = defineRule({
47888
46946
  id: "query-no-rest-destructuring",
47889
46947
  title: "Rest destructuring on query result",
@@ -47895,7 +46953,7 @@ const queryNoRestDestructuring = defineRule({
47895
46953
  if (!isNodeOfType(node.id, "ObjectPattern")) return;
47896
46954
  if (!node.init) return;
47897
46955
  if (!node.id.properties?.some((property) => isNodeOfType(property, "RestElement"))) return;
47898
- const hookName = resolveTanstackQueryHookNameFromInitializer(node.init, context.scopes);
46956
+ const hookName = resolveHookNameFromInitializer(node.init, context.scopes);
47899
46957
  if (!hookName) return;
47900
46958
  context.report({
47901
46959
  node: node.id,
@@ -48030,8 +47088,8 @@ const queryStableQueryClient = defineRule({
48030
47088
  create: (context) => ({ NewExpression(node) {
48031
47089
  if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "QueryClient") return;
48032
47090
  if (isStableHookWrapperArgument(node)) return;
48033
- let enclosingFunction = findEnclosingFunction$1(node);
48034
- while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
47091
+ let enclosingFunction = findEnclosingFunction(node);
47092
+ while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
48035
47093
  if (!enclosingFunction) return;
48036
47094
  if (!isComponentFunction(enclosingFunction)) return;
48037
47095
  context.report({
@@ -48126,7 +47184,7 @@ const reactCompilerNoManualMemoization = defineRule({
48126
47184
  const comparatorArgument = node.arguments?.[1];
48127
47185
  if (comparatorArgument && !isNullishComparatorArgument(comparatorArgument)) return;
48128
47186
  } else {
48129
- const enclosingFunction = findEnclosingFunction$1(node);
47187
+ const enclosingFunction = findEnclosingFunction(node);
48130
47188
  if (!enclosingFunction || !isCompilerInferableFunction(enclosingFunction)) return;
48131
47189
  }
48132
47190
  const removalMessage = REMOVAL_MESSAGE_BY_REACT_API_NAME.get(apiName);
@@ -48139,7 +47197,7 @@ const reactCompilerNoManualMemoization = defineRule({
48139
47197
  });
48140
47198
  //#endregion
48141
47199
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
48142
- const MESSAGE$7 = "This JSX crashes because `React` isn't in scope.";
47200
+ const MESSAGE$6 = "This JSX crashes because `React` isn't in scope.";
48143
47201
  const reactInJsxScope = defineRule({
48144
47202
  id: "react-in-jsx-scope",
48145
47203
  title: "React not in scope for JSX",
@@ -48151,190 +47209,19 @@ const reactInJsxScope = defineRule({
48151
47209
  if (findVariableInitializer(node, "React")) return;
48152
47210
  context.report({
48153
47211
  node: node.name,
48154
- message: MESSAGE$7
47212
+ message: MESSAGE$6
48155
47213
  });
48156
47214
  },
48157
47215
  JSXFragment(node) {
48158
47216
  if (findVariableInitializer(node, "React")) return;
48159
47217
  context.report({
48160
47218
  node: node.openingFragment,
48161
- message: MESSAGE$7
47219
+ message: MESSAGE$6
48162
47220
  });
48163
47221
  }
48164
47222
  })
48165
47223
  });
48166
47224
  //#endregion
48167
- //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
48168
- const MESSAGE$6 = "React Markdown parses dynamic raw HTML when `rehype-raw` is enabled. Add `rehype-sanitize` to `rehypePlugins` or sanitize the markdown before rendering it.";
48169
- const REACT_MARKDOWN_MODULE = "react-markdown";
48170
- const REHYPE_RAW_MODULE = "rehype-raw";
48171
- const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
48172
- const DOMPURIFY_MODULES = new Set(["dompurify", "isomorphic-dompurify"]);
48173
- const REACT_MARKDOWN_NAMED_EXPORTS = new Set(["MarkdownAsync", "MarkdownHooks"]);
48174
- const REACT_MARKDOWN_NAMESPACE_EXPORTS = new Set(["default", ...REACT_MARKDOWN_NAMED_EXPORTS]);
48175
- const DEFAULT_EXPORT_NAMES = new Set(["default"]);
48176
- const getImportDeclaration = (symbol) => {
48177
- if (symbol.kind !== "import") return null;
48178
- const importDeclaration = symbol.declarationNode.parent;
48179
- return isNodeOfType(importDeclaration, "ImportDeclaration") ? importDeclaration : null;
48180
- };
48181
- const isImportFromModule = (symbol, moduleName) => getImportDeclaration(symbol)?.source.value === moduleName;
48182
- const isDefaultImportSymbol = (symbol, moduleName) => {
48183
- if (!isImportFromModule(symbol, moduleName)) return false;
48184
- return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || getImportedName(symbol.declarationNode) === "default";
48185
- };
48186
- const resolveImportedIdentifier = (node, scopes) => {
48187
- if (!isNodeOfType(node, "Identifier") && !isNodeOfType(node, "JSXIdentifier")) return null;
48188
- const symbol = resolveConstIdentifierAlias(node, scopes);
48189
- return symbol?.kind === "import" ? symbol : null;
48190
- };
48191
- const getStaticMemberParts = (node) => {
48192
- if (isNodeOfType(node, "MemberExpression")) {
48193
- if (node.computed || !isNodeOfType(node.property, "Identifier")) return null;
48194
- return {
48195
- object: node.object,
48196
- propertyName: node.property.name
48197
- };
48198
- }
48199
- if (isNodeOfType(node, "JSXMemberExpression")) {
48200
- if (!isNodeOfType(node.property, "JSXIdentifier")) return null;
48201
- return {
48202
- object: node.object,
48203
- propertyName: node.property.name
48204
- };
48205
- }
48206
- return null;
48207
- };
48208
- const isNamespaceMemberFromModule = (node, moduleName, memberNames, scopes) => {
48209
- const memberParts = getStaticMemberParts(node);
48210
- if (!memberParts || !memberNames.has(memberParts.propertyName)) return false;
48211
- const namespaceSymbol = resolveImportedIdentifier(memberParts.object, scopes);
48212
- return Boolean(namespaceSymbol && isImportFromModule(namespaceSymbol, moduleName) && isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier"));
48213
- };
48214
- const isReactMarkdownComponent = (node, scopes) => {
48215
- if (isNodeOfType(node, "JSXIdentifier")) {
48216
- const symbol = resolveImportedIdentifier(node, scopes);
48217
- if (!symbol || !isImportFromModule(symbol, REACT_MARKDOWN_MODULE)) return false;
48218
- if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return true;
48219
- const importedName = getImportedName(symbol.declarationNode);
48220
- return importedName === "default" || Boolean(importedName && REACT_MARKDOWN_NAMED_EXPORTS.has(importedName));
48221
- }
48222
- return isNamespaceMemberFromModule(node, REACT_MARKDOWN_MODULE, REACT_MARKDOWN_NAMESPACE_EXPORTS, scopes);
48223
- };
48224
- const isPluginFromModule = (rawNode, moduleName, scopes, visitedSymbolIds) => {
48225
- const node = stripParenExpression(rawNode);
48226
- if (isNodeOfType(node, "Identifier")) {
48227
- const symbol = resolveConstIdentifierAlias(node, scopes);
48228
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
48229
- if (isDefaultImportSymbol(symbol, moduleName)) return true;
48230
- if (symbol.kind !== "const" || !symbol.initializer) return false;
48231
- visitedSymbolIds.add(symbol.id);
48232
- return isPluginFromModule(symbol.initializer, moduleName, scopes, visitedSymbolIds);
48233
- }
48234
- if (isNamespaceMemberFromModule(node, moduleName, DEFAULT_EXPORT_NAMES, scopes)) return true;
48235
- if (!isNodeOfType(node, "ArrayExpression")) return false;
48236
- for (const element of node.elements) {
48237
- if (!element || isNodeOfType(element, "SpreadElement")) continue;
48238
- return isPluginFromModule(element, moduleName, scopes, visitedSymbolIds);
48239
- }
48240
- return false;
48241
- };
48242
- const collectPluginEntries = (rawNode, scopes, visitedSymbolIds) => {
48243
- const node = stripParenExpression(rawNode);
48244
- if (isNodeOfType(node, "Identifier")) {
48245
- const symbol = scopes.referenceFor(node)?.resolvedSymbol;
48246
- if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
48247
- visitedSymbolIds.add(symbol.id);
48248
- return collectPluginEntries(symbol.initializer, scopes, visitedSymbolIds);
48249
- }
48250
- if (!isNodeOfType(node, "ArrayExpression")) return null;
48251
- const entries = [];
48252
- for (const element of node.elements) {
48253
- if (!element) continue;
48254
- if (isNodeOfType(element, "SpreadElement")) {
48255
- const spreadEntries = collectPluginEntries(element.argument, scopes, new Set(visitedSymbolIds));
48256
- if (spreadEntries === null) return null;
48257
- entries.push(...spreadEntries);
48258
- continue;
48259
- }
48260
- entries.push(element);
48261
- }
48262
- return entries;
48263
- };
48264
- const findEffectiveExplicitAttribute = (attributes, attributeName) => {
48265
- for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
48266
- const attribute = attributes[attributeIndex];
48267
- if (!attribute || isNodeOfType(attribute, "JSXSpreadAttribute")) return null;
48268
- if (isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && attribute.name.name === attributeName) return attribute;
48269
- }
48270
- return null;
48271
- };
48272
- const getAttributeExpression = (attribute) => {
48273
- if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
48274
- return isNodeOfType(attribute.value.expression, "JSXEmptyExpression") ? null : attribute.value.expression;
48275
- };
48276
- const isDomPurifyNamespace = (node, scopes) => {
48277
- const symbol = resolveImportedIdentifier(node, scopes);
48278
- if (!symbol) return false;
48279
- const importDeclaration = getImportDeclaration(symbol);
48280
- if (!importDeclaration || !DOMPURIFY_MODULES.has(String(importDeclaration.source.value))) return false;
48281
- return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
48282
- };
48283
- const isDomPurifySanitizeCall = (node, scopes) => {
48284
- if (!isNodeOfType(node, "CallExpression")) return false;
48285
- const memberParts = getStaticMemberParts(stripParenExpression(node.callee));
48286
- return Boolean(memberParts && memberParts.propertyName === "sanitize" && isDomPurifyNamespace(memberParts.object, scopes));
48287
- };
48288
- const isStaticOrSanitizedMarkdownExpression = (rawNode, scopes, visitedSymbolIds) => {
48289
- const node = stripParenExpression(rawNode);
48290
- if (isNodeOfType(node, "Literal")) return true;
48291
- if (isNodeOfType(node, "TemplateLiteral")) return node.expressions.every((expression) => isStaticOrSanitizedMarkdownExpression(expression, scopes, new Set(visitedSymbolIds)));
48292
- if (isDomPurifySanitizeCall(node, scopes)) return true;
48293
- if (isNodeOfType(node, "ConditionalExpression")) return isStaticOrSanitizedMarkdownExpression(node.consequent, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.alternate, scopes, new Set(visitedSymbolIds));
48294
- if (isNodeOfType(node, "BinaryExpression") && node.operator === "+") return isStaticOrSanitizedMarkdownExpression(node.left, scopes, new Set(visitedSymbolIds)) && isStaticOrSanitizedMarkdownExpression(node.right, scopes, new Set(visitedSymbolIds));
48295
- if (!isNodeOfType(node, "Identifier")) return false;
48296
- const symbol = scopes.referenceFor(node)?.resolvedSymbol;
48297
- if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
48298
- visitedSymbolIds.add(symbol.id);
48299
- return isStaticOrSanitizedMarkdownExpression(symbol.initializer, scopes, visitedSymbolIds);
48300
- };
48301
- const hasDynamicUnsanitizedChildren = (openingElement, scopes) => {
48302
- const jsxElement = openingElement.parent;
48303
- if (!isNodeOfType(jsxElement, "JSXElement")) return false;
48304
- const meaningfulChildren = jsxElement.children.filter((child) => !isNodeOfType(child, "JSXText") || child.value.trim().length > 0);
48305
- if (meaningfulChildren.length > 0) return meaningfulChildren.some((child) => {
48306
- if (isNodeOfType(child, "JSXText")) return false;
48307
- if (!isNodeOfType(child, "JSXExpressionContainer")) return true;
48308
- if (isNodeOfType(child.expression, "JSXEmptyExpression")) return false;
48309
- return !isStaticOrSanitizedMarkdownExpression(child.expression, scopes, /* @__PURE__ */ new Set());
48310
- });
48311
- const childrenAttribute = findEffectiveExplicitAttribute(openingElement.attributes, "children");
48312
- if (!childrenAttribute) return false;
48313
- const childrenExpression = getAttributeExpression(childrenAttribute);
48314
- return Boolean(childrenExpression && !isStaticOrSanitizedMarkdownExpression(childrenExpression, scopes, /* @__PURE__ */ new Set()));
48315
- };
48316
- const reactMarkdownUnsanitizedRawHtml = defineRule({
48317
- id: "react-markdown-unsanitized-raw-html",
48318
- title: "Unsanitized raw HTML in React Markdown",
48319
- severity: "warn",
48320
- recommendation: "Add `rehype-sanitize` to `rehypePlugins` or sanitize dynamic markdown before rendering. `skipHtml` does not disable HTML already parsed by `rehype-raw`.",
48321
- create: skipNonProductionFiles((context) => ({ JSXOpeningElement(node) {
48322
- if (!isReactMarkdownComponent(node.name, context.scopes)) return;
48323
- const pluginsAttribute = findEffectiveExplicitAttribute(node.attributes, "rehypePlugins");
48324
- if (!pluginsAttribute) return;
48325
- const pluginsExpression = getAttributeExpression(pluginsAttribute);
48326
- if (!pluginsExpression) return;
48327
- const pluginEntries = collectPluginEntries(pluginsExpression, context.scopes, /* @__PURE__ */ new Set());
48328
- if (pluginEntries === null) return;
48329
- if (!pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_RAW_MODULE, context.scopes, /* @__PURE__ */ new Set()))) return;
48330
- if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
48331
- context.report({
48332
- node,
48333
- message: MESSAGE$6
48334
- });
48335
- } }))
48336
- });
48337
- //#endregion
48338
47225
  //#region src/plugin/utils/collect-react-redux-selector-aliases.ts
48339
47226
  const REACT_REDUX_MODULE = "react-redux";
48340
47227
  const collectReactReduxSelectorAliases = (programRoot) => {
@@ -51231,7 +50118,7 @@ const isReactNativeDimensionsCallee = (node, callee) => {
51231
50118
  };
51232
50119
  const STYLE_FACTORY_CALLEE_PATTERN$1 = /(?:^|\.)(?:make|create)(?:Use)?Styles$/;
51233
50120
  const isInsideStyleFactoryCallback = (node) => {
51234
- const enclosingFunction = findEnclosingFunction$1(node);
50121
+ const enclosingFunction = findEnclosingFunction(node);
51235
50122
  if (!enclosingFunction) return false;
51236
50123
  const callExpression = enclosingFunction.parent;
51237
50124
  if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) return false;
@@ -56762,6 +55649,22 @@ const isInsideClassComponent = (node) => {
56762
55649
  }
56763
55650
  return false;
56764
55651
  };
55652
+ const hasShortCircuitAncestor = (descendant, ancestor) => {
55653
+ let current = descendant.parent;
55654
+ while (current && current !== ancestor) {
55655
+ if (isNodeOfType(current, "ConditionalExpression") && (isWithinRange(descendant, current.consequent) || isWithinRange(descendant, current.alternate))) return true;
55656
+ if (isNodeOfType(current, "LogicalExpression") && (current.operator === "&&" || current.operator === "||" || current.operator === "??") && isWithinRange(descendant, current.right)) return true;
55657
+ if (isNodeOfType(current, "AssignmentPattern") && isWithinRange(descendant, current.right)) return true;
55658
+ current = current.parent ?? null;
55659
+ }
55660
+ return false;
55661
+ };
55662
+ const isWithinRange = (descendant, sibling) => {
55663
+ const descendantSpan = descendant;
55664
+ const siblingSpan = sibling;
55665
+ if (typeof descendantSpan.start !== "number" || typeof siblingSpan.start !== "number" || typeof siblingSpan.end !== "number") return false;
55666
+ return descendantSpan.start >= siblingSpan.start && (descendantSpan.end ?? siblingSpan.end) <= siblingSpan.end;
55667
+ };
56765
55668
  const isInsideTry = (descendant, ancestor) => {
56766
55669
  let current = descendant.parent;
56767
55670
  while (current && current !== ancestor) {
@@ -56953,7 +55856,7 @@ const rulesOfHooks = defineRule({
56953
55856
  });
56954
55857
  return;
56955
55858
  }
56956
- if (isNodeConditionallyExecuted(node, enclosing.node)) {
55859
+ if (hasShortCircuitAncestor(node, enclosing.node)) {
56957
55860
  context.report({
56958
55861
  node: node.callee,
56959
55862
  message: buildConditionalMessage(hookName)
@@ -58087,14 +56990,12 @@ const declarationAwaitsRequestScopedCall = (declaration) => {
58087
56990
  }
58088
56991
  return false;
58089
56992
  };
58090
- const declarationAwaitsGate = (declaration, context) => {
56993
+ const declarationAwaitsGate = (declaration) => {
58091
56994
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
58092
56995
  for (const declarator of declaration.declarations ?? []) {
58093
56996
  if (!isNodeOfType(declarator.init, "AwaitExpression")) continue;
58094
56997
  const argument = declarator.init.argument;
58095
56998
  if (!isNodeOfType(argument, "CallExpression")) continue;
58096
- if (hasPossibleStaticMemberCallWrite(argument, context.scopes)) return true;
58097
- if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) continue;
58098
56999
  const calleeName = getCalleeName$2(argument);
58099
57000
  if (!calleeName) continue;
58100
57001
  if (isAuthGuardName(calleeName)) return true;
@@ -58122,7 +57023,7 @@ const serverSequentialIndependentAwait = defineRule({
58122
57023
  if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
58123
57024
  if (declarationAwaitsExistingPromise(nextStatement)) continue;
58124
57025
  if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
58125
- if (declarationAwaitsGate(currentStatement, context)) continue;
57026
+ if (declarationAwaitsGate(currentStatement)) continue;
58126
57027
  context.report({
58127
57028
  node: nextStatement,
58128
57029
  message: "This await doesn't use the previous result, so your users wait twice as long for nothing."
@@ -59108,7 +58009,7 @@ const tanstackStartNoNavigateInRender = defineRule({
59108
58009
  const isReturnedFromCustomHook = (functionNode) => {
59109
58010
  const parent = functionNode.parent;
59110
58011
  if (isNodeOfType(parent, "ReturnStatement")) {
59111
- const outerFunction = findEnclosingFunction$1(parent);
58012
+ const outerFunction = findEnclosingFunction(parent);
59112
58013
  const hookName = outerFunction ? getFunctionBindingName$1(outerFunction) : null;
59113
58014
  return Boolean(hookName && HOOK_NAME_PATTERN$3.test(hookName));
59114
58015
  }
@@ -59125,13 +58026,13 @@ const tanstackStartNoNavigateInRender = defineRule({
59125
58026
  return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
59126
58027
  };
59127
58028
  const isDeferredNavigateCall = (callNode) => {
59128
- let enclosingFunction = findEnclosingFunction$1(callNode);
58029
+ let enclosingFunction = findEnclosingFunction(callNode);
59129
58030
  while (enclosingFunction) {
59130
58031
  if (isDeferredCallbackPosition(enclosingFunction)) return true;
59131
58032
  if (isWiredAsEventHandler(enclosingFunction)) return true;
59132
58033
  if (isReturnedFromCustomHook(enclosingFunction)) return true;
59133
58034
  if (!isSynchronouslyInvokedAnonymousWrapper(enclosingFunction)) return false;
59134
- enclosingFunction = findEnclosingFunction$1(enclosingFunction);
58035
+ enclosingFunction = findEnclosingFunction(enclosingFunction);
59135
58036
  }
59136
58037
  return false;
59137
58038
  };
@@ -63570,17 +62471,6 @@ const reactDoctorRules = [
63570
62471
  requires: [...new Set(["react", ...reactInJsxScope.requires ?? []])]
63571
62472
  }
63572
62473
  },
63573
- {
63574
- key: "react-doctor/react-markdown-unsanitized-raw-html",
63575
- id: "react-markdown-unsanitized-raw-html",
63576
- source: "react-doctor",
63577
- originallyExternal: false,
63578
- rule: {
63579
- ...reactMarkdownUnsanitizedRawHtml,
63580
- framework: "global",
63581
- category: "Security"
63582
- }
63583
- },
63584
62474
  {
63585
62475
  key: "react-doctor/redux-useselector-inline-derivation",
63586
62476
  id: "redux-useselector-inline-derivation",