oxlint-plugin-react-doctor 0.7.8 → 0.7.9-dev.61111f1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2665 -1068
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2339,8 +2339,309 @@ const anchorAmbiguousText = defineRule({
2339
2339
  }
2340
2340
  });
2341
2341
  //#endregion
2342
+ //#region src/plugin/constants/aria-roles.ts
2343
+ const VALID_ARIA_ROLES = new Set([
2344
+ "alert",
2345
+ "alertdialog",
2346
+ "application",
2347
+ "article",
2348
+ "banner",
2349
+ "blockquote",
2350
+ "button",
2351
+ "caption",
2352
+ "cell",
2353
+ "checkbox",
2354
+ "code",
2355
+ "columnheader",
2356
+ "combobox",
2357
+ "complementary",
2358
+ "contentinfo",
2359
+ "definition",
2360
+ "deletion",
2361
+ "dialog",
2362
+ "directory",
2363
+ "doc-abstract",
2364
+ "doc-acknowledgments",
2365
+ "doc-afterword",
2366
+ "doc-appendix",
2367
+ "doc-backlink",
2368
+ "doc-biblioentry",
2369
+ "doc-bibliography",
2370
+ "doc-biblioref",
2371
+ "doc-chapter",
2372
+ "doc-colophon",
2373
+ "doc-conclusion",
2374
+ "doc-cover",
2375
+ "doc-credit",
2376
+ "doc-credits",
2377
+ "doc-dedication",
2378
+ "doc-endnote",
2379
+ "doc-endnotes",
2380
+ "doc-epigraph",
2381
+ "doc-epilogue",
2382
+ "doc-errata",
2383
+ "doc-example",
2384
+ "doc-footnote",
2385
+ "doc-foreword",
2386
+ "doc-glossary",
2387
+ "doc-glossref",
2388
+ "doc-index",
2389
+ "doc-introduction",
2390
+ "doc-noteref",
2391
+ "doc-notice",
2392
+ "doc-pagebreak",
2393
+ "doc-pagelist",
2394
+ "doc-part",
2395
+ "doc-preface",
2396
+ "doc-prologue",
2397
+ "doc-pullquote",
2398
+ "doc-qna",
2399
+ "doc-subtitle",
2400
+ "doc-tip",
2401
+ "doc-toc",
2402
+ "document",
2403
+ "emphasis",
2404
+ "feed",
2405
+ "figure",
2406
+ "form",
2407
+ "generic",
2408
+ "graphics-document",
2409
+ "graphics-object",
2410
+ "graphics-symbol",
2411
+ "grid",
2412
+ "gridcell",
2413
+ "group",
2414
+ "heading",
2415
+ "img",
2416
+ "insertion",
2417
+ "link",
2418
+ "list",
2419
+ "listbox",
2420
+ "listitem",
2421
+ "log",
2422
+ "main",
2423
+ "mark",
2424
+ "marquee",
2425
+ "math",
2426
+ "menu",
2427
+ "menubar",
2428
+ "menuitem",
2429
+ "menuitemcheckbox",
2430
+ "menuitemradio",
2431
+ "meter",
2432
+ "navigation",
2433
+ "none",
2434
+ "note",
2435
+ "option",
2436
+ "paragraph",
2437
+ "presentation",
2438
+ "progressbar",
2439
+ "radio",
2440
+ "radiogroup",
2441
+ "region",
2442
+ "row",
2443
+ "rowgroup",
2444
+ "rowheader",
2445
+ "scrollbar",
2446
+ "search",
2447
+ "searchbox",
2448
+ "separator",
2449
+ "slider",
2450
+ "spinbutton",
2451
+ "status",
2452
+ "strong",
2453
+ "subscript",
2454
+ "superscript",
2455
+ "switch",
2456
+ "tab",
2457
+ "table",
2458
+ "tablist",
2459
+ "tabpanel",
2460
+ "term",
2461
+ "textbox",
2462
+ "time",
2463
+ "timer",
2464
+ "toolbar",
2465
+ "tooltip",
2466
+ "tree",
2467
+ "treegrid",
2468
+ "treeitem"
2469
+ ]);
2470
+ const INTERACTIVE_ROLES = new Set([
2471
+ "button",
2472
+ "checkbox",
2473
+ "columnheader",
2474
+ "combobox",
2475
+ "grid",
2476
+ "gridcell",
2477
+ "link",
2478
+ "listbox",
2479
+ "menu",
2480
+ "menubar",
2481
+ "menuitem",
2482
+ "menuitemcheckbox",
2483
+ "menuitemradio",
2484
+ "option",
2485
+ "radio",
2486
+ "radiogroup",
2487
+ "row",
2488
+ "rowheader",
2489
+ "scrollbar",
2490
+ "searchbox",
2491
+ "separator",
2492
+ "slider",
2493
+ "spinbutton",
2494
+ "switch",
2495
+ "tab",
2496
+ "tablist",
2497
+ "textbox",
2498
+ "toolbar",
2499
+ "tree",
2500
+ "treegrid",
2501
+ "treeitem"
2502
+ ]);
2503
+ const NON_INTERACTIVE_ROLES = new Set([
2504
+ "alert",
2505
+ "alertdialog",
2506
+ "application",
2507
+ "article",
2508
+ "banner",
2509
+ "blockquote",
2510
+ "caption",
2511
+ "cell",
2512
+ "complementary",
2513
+ "contentinfo",
2514
+ "definition",
2515
+ "deletion",
2516
+ "dialog",
2517
+ "directory",
2518
+ "document",
2519
+ "feed",
2520
+ "figure",
2521
+ "form",
2522
+ "group",
2523
+ "heading",
2524
+ "img",
2525
+ "insertion",
2526
+ "list",
2527
+ "listitem",
2528
+ "log",
2529
+ "main",
2530
+ "marquee",
2531
+ "math",
2532
+ "navigation",
2533
+ "note",
2534
+ "paragraph",
2535
+ "progressbar",
2536
+ "region",
2537
+ "rowgroup",
2538
+ "search",
2539
+ "status",
2540
+ "table",
2541
+ "tabpanel",
2542
+ "term",
2543
+ "time",
2544
+ "timer",
2545
+ "tooltip"
2546
+ ]);
2547
+ const ABSTRACT_ROLES = new Set([
2548
+ "command",
2549
+ "composite",
2550
+ "input",
2551
+ "landmark",
2552
+ "range",
2553
+ "roletype",
2554
+ "section",
2555
+ "sectionhead",
2556
+ "select",
2557
+ "structure",
2558
+ "widget",
2559
+ "window"
2560
+ ]);
2561
+ const PRESENTATION_ROLES$1 = new Set(["presentation", "none"]);
2562
+ //#endregion
2563
+ //#region src/plugin/utils/read-static-boolean.ts
2564
+ const readStaticBoolean = (node) => {
2565
+ if (!node) return null;
2566
+ const unwrappedNode = stripParenExpression(node);
2567
+ if (!isNodeOfType(unwrappedNode, "Literal") || typeof unwrappedNode.value !== "boolean") return null;
2568
+ return unwrappedNode.value;
2569
+ };
2570
+ //#endregion
2571
+ //#region src/plugin/utils/get-jsx-prop-static-string-values.ts
2572
+ const MAX_CONST_RESOLUTION_HOPS = 4;
2573
+ const resolveStaticStringValues = (rawExpression, scopes, maximumConstAliases, shouldFoldStaticConditions) => {
2574
+ const staticStringValues = [];
2575
+ const workItems = [{
2576
+ expression: rawExpression,
2577
+ remainingConstAliases: maximumConstAliases,
2578
+ resolvingSymbols: /* @__PURE__ */ new Set()
2579
+ }];
2580
+ while (workItems.length > 0) {
2581
+ const workItem = workItems.pop();
2582
+ if (!workItem) continue;
2583
+ const expression = stripParenExpression(workItem.expression);
2584
+ if (isNodeOfType(expression, "Literal")) {
2585
+ if (typeof expression.value !== "string") return null;
2586
+ staticStringValues.push(expression.value);
2587
+ continue;
2588
+ }
2589
+ if (isNodeOfType(expression, "TemplateLiteral")) {
2590
+ const staticValue = getStaticTemplateLiteralValue(expression);
2591
+ if (staticValue === null) return null;
2592
+ staticStringValues.push(staticValue);
2593
+ continue;
2594
+ }
2595
+ if (isNodeOfType(expression, "ConditionalExpression")) {
2596
+ const staticTestValue = shouldFoldStaticConditions ? readStaticBoolean(expression.test) : null;
2597
+ if (staticTestValue !== null) {
2598
+ workItems.push({
2599
+ expression: staticTestValue ? expression.consequent : expression.alternate,
2600
+ remainingConstAliases: workItem.remainingConstAliases,
2601
+ resolvingSymbols: workItem.resolvingSymbols
2602
+ });
2603
+ continue;
2604
+ }
2605
+ workItems.push({
2606
+ expression: expression.alternate,
2607
+ remainingConstAliases: workItem.remainingConstAliases,
2608
+ resolvingSymbols: new Set(workItem.resolvingSymbols)
2609
+ });
2610
+ workItems.push({
2611
+ expression: expression.consequent,
2612
+ remainingConstAliases: workItem.remainingConstAliases,
2613
+ resolvingSymbols: new Set(workItem.resolvingSymbols)
2614
+ });
2615
+ continue;
2616
+ }
2617
+ if (isNodeOfType(expression, "Identifier")) {
2618
+ const symbol = scopes.referenceFor(expression)?.resolvedSymbol;
2619
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier || workItem.remainingConstAliases === 0 || workItem.resolvingSymbols.has(symbol)) return null;
2620
+ workItem.resolvingSymbols.add(symbol);
2621
+ workItems.push({
2622
+ expression: symbol.initializer,
2623
+ remainingConstAliases: workItem.remainingConstAliases === null ? null : workItem.remainingConstAliases - 1,
2624
+ resolvingSymbols: workItem.resolvingSymbols
2625
+ });
2626
+ continue;
2627
+ }
2628
+ return null;
2629
+ }
2630
+ return staticStringValues;
2631
+ };
2632
+ const getJsxPropStaticStringValuesWithMode = (attribute, scopes, maximumConstAliases, shouldFoldStaticConditions) => {
2633
+ const value = attribute.value;
2634
+ if (!value) return null;
2635
+ if (isNodeOfType(value, "Literal")) return typeof value.value === "string" ? [value.value] : null;
2636
+ if (isNodeOfType(value, "JSXExpressionContainer")) return resolveStaticStringValues(value.expression, scopes, maximumConstAliases, shouldFoldStaticConditions);
2637
+ return null;
2638
+ };
2639
+ const getJsxPropStaticStringValues = (attribute, scopes) => getJsxPropStaticStringValuesWithMode(attribute, scopes, MAX_CONST_RESOLUTION_HOPS, false);
2640
+ const getJsxPropExhaustiveStaticStringValues = (attribute, scopes) => getJsxPropStaticStringValuesWithMode(attribute, scopes, null, true);
2641
+ //#endregion
2342
2642
  //#region src/plugin/rules/a11y/anchor-has-content.ts
2343
2643
  const MESSAGE$62 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2644
+ const hasLinkRole = (roleValue) => roleValue.trim().split(/\s+/).find((roleToken) => VALID_ARIA_ROLES.has(roleToken)) === "link";
2344
2645
  const isTransComponentsTemplate = (node) => {
2345
2646
  let current = node.parent;
2346
2647
  while (current) {
@@ -2358,7 +2659,7 @@ const anchorHasContent = defineRule({
2358
2659
  title: "Anchor has no content",
2359
2660
  tags: ["react-jsx-only"],
2360
2661
  severity: "warn",
2361
- recommendation: "Put readable text inside every `<a>`.",
2662
+ recommendation: "Put readable text inside every link.",
2362
2663
  category: "Accessibility",
2363
2664
  create: (context) => {
2364
2665
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -2366,6 +2667,11 @@ const anchorHasContent = defineRule({
2366
2667
  if (isTestlikeFile) return;
2367
2668
  const opening = node.openingElement;
2368
2669
  if (getElementType(opening, context.settings) !== "a") return;
2670
+ const hrefAttribute = hasJsxPropIgnoreCase(opening.attributes, "href");
2671
+ const roleAttribute = hasJsxPropIgnoreCase(opening.attributes, "role");
2672
+ const roleValues = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [];
2673
+ const canHaveLinkRole = roleValues === null || roleValues.some((roleValue) => hasLinkRole(roleValue));
2674
+ if (!hrefAttribute && !canHaveLinkRole) return;
2369
2675
  if (isHiddenFromScreenReader(opening, context.settings)) return;
2370
2676
  if (objectHasAccessibleChild(node, context.settings)) return;
2371
2677
  for (const attribute of [
@@ -2382,39 +2688,6 @@ const anchorHasContent = defineRule({
2382
2688
  }
2383
2689
  });
2384
2690
  //#endregion
2385
- //#region src/plugin/utils/get-jsx-prop-static-string-values.ts
2386
- const MAX_CONST_RESOLUTION_HOPS = 4;
2387
- const resolveStaticStringValues = (rawExpression, scopes, remainingHops) => {
2388
- const expression = stripParenExpression(rawExpression);
2389
- if (isNodeOfType(expression, "Literal")) return typeof expression.value === "string" ? [expression.value] : null;
2390
- if (isNodeOfType(expression, "TemplateLiteral")) {
2391
- const staticValue = getStaticTemplateLiteralValue(expression);
2392
- return staticValue === null ? null : [staticValue];
2393
- }
2394
- if (isNodeOfType(expression, "ConditionalExpression")) {
2395
- const consequentValues = resolveStaticStringValues(expression.consequent, scopes, remainingHops);
2396
- if (consequentValues === null) return null;
2397
- const alternateValues = resolveStaticStringValues(expression.alternate, scopes, remainingHops);
2398
- if (alternateValues === null) return null;
2399
- return [...consequentValues, ...alternateValues];
2400
- }
2401
- if (isNodeOfType(expression, "Identifier")) {
2402
- if (remainingHops === 0) return null;
2403
- const symbol = scopes.referenceFor(expression)?.resolvedSymbol;
2404
- if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
2405
- if (!isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
2406
- return resolveStaticStringValues(symbol.initializer, scopes, remainingHops - 1);
2407
- }
2408
- return null;
2409
- };
2410
- const getJsxPropStaticStringValues = (attribute, scopes) => {
2411
- const value = attribute.value;
2412
- if (!value) return null;
2413
- if (isNodeOfType(value, "Literal")) return typeof value.value === "string" ? [value.value] : null;
2414
- if (isNodeOfType(value, "JSXExpressionContainer")) return resolveStaticStringValues(value.expression, scopes, MAX_CONST_RESOLUTION_HOPS);
2415
- return null;
2416
- };
2417
- //#endregion
2418
2691
  //#region src/plugin/rules/a11y/anchor-is-valid.ts
2419
2692
  const MESSAGE_MISSING_HREF = "Keyboard users can't reach this link because it has no `href`, so add a real `href` (or use `<button>` for actions).";
2420
2693
  const MESSAGE_INCORRECT_HREF = "Keyboard users can't reach this link because its `href` goes nowhere (`#`, `javascript:`, or empty), so point it at a real destination.";
@@ -2737,6 +3010,95 @@ const NON_INTERACTIVE_ELEMENTS = new Set([
2737
3010
  "ul"
2738
3011
  ]);
2739
3012
  //#endregion
3013
+ //#region src/plugin/utils/get-direct-const-initializer.ts
3014
+ const getDirectConstInitializer = (symbol) => {
3015
+ if (symbol.kind !== "const" || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
3016
+ return symbol.initializer;
3017
+ };
3018
+ //#endregion
3019
+ //#region src/plugin/utils/can-content-editable-be-tabbable.ts
3020
+ const ENABLED_CONTENT_EDITABLE_VALUES = new Set([
3021
+ "",
3022
+ "plaintext-only",
3023
+ "true"
3024
+ ]);
3025
+ const ENABLED_CONTENT_EDITABLE = {
3026
+ canBeDisabled: false,
3027
+ canBeEnabled: true,
3028
+ canBeInherited: false
3029
+ };
3030
+ const DISABLED_CONTENT_EDITABLE = {
3031
+ canBeDisabled: true,
3032
+ canBeEnabled: false,
3033
+ canBeInherited: false
3034
+ };
3035
+ const INHERITED_CONTENT_EDITABLE = {
3036
+ canBeDisabled: false,
3037
+ canBeEnabled: false,
3038
+ canBeInherited: true
3039
+ };
3040
+ const UNKNOWN_CONTENT_EDITABLE = {
3041
+ canBeDisabled: true,
3042
+ canBeEnabled: true,
3043
+ canBeInherited: true
3044
+ };
3045
+ const mergeContentEditablePossibilities = (left, right) => ({
3046
+ canBeDisabled: left.canBeDisabled || right.canBeDisabled,
3047
+ canBeEnabled: left.canBeEnabled || right.canBeEnabled,
3048
+ canBeInherited: left.canBeInherited || right.canBeInherited
3049
+ });
3050
+ const resolveContentEditableExpression = (rawExpression, scopes, visitedSymbolIds) => {
3051
+ const expression = stripParenExpression(rawExpression);
3052
+ if (isNodeOfType(expression, "Literal")) {
3053
+ if (typeof expression.value === "boolean") return expression.value ? ENABLED_CONTENT_EDITABLE : DISABLED_CONTENT_EDITABLE;
3054
+ if (typeof expression.value === "string") {
3055
+ const contentEditableValue = expression.value.toLowerCase();
3056
+ if (ENABLED_CONTENT_EDITABLE_VALUES.has(contentEditableValue)) return ENABLED_CONTENT_EDITABLE;
3057
+ return contentEditableValue === "false" ? DISABLED_CONTENT_EDITABLE : INHERITED_CONTENT_EDITABLE;
3058
+ }
3059
+ return INHERITED_CONTENT_EDITABLE;
3060
+ }
3061
+ if (isNodeOfType(expression, "ConditionalExpression")) return mergeContentEditablePossibilities(resolveContentEditableExpression(expression.consequent, scopes, visitedSymbolIds), resolveContentEditableExpression(expression.alternate, scopes, visitedSymbolIds));
3062
+ if (isNodeOfType(expression, "Identifier")) {
3063
+ const symbol = scopes.referenceFor(expression)?.resolvedSymbol;
3064
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return UNKNOWN_CONTENT_EDITABLE;
3065
+ const initializer = getDirectConstInitializer(symbol);
3066
+ if (!initializer) return UNKNOWN_CONTENT_EDITABLE;
3067
+ const nextVisitedSymbolIds = new Set(visitedSymbolIds);
3068
+ nextVisitedSymbolIds.add(symbol.id);
3069
+ return resolveContentEditableExpression(initializer, scopes, nextVisitedSymbolIds);
3070
+ }
3071
+ return UNKNOWN_CONTENT_EDITABLE;
3072
+ };
3073
+ const getContentEditablePossibilities = (node, scopes) => {
3074
+ const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
3075
+ if (!attribute) return null;
3076
+ if (!attribute.value) return ENABLED_CONTENT_EDITABLE;
3077
+ if (isNodeOfType(attribute.value, "Literal")) return resolveContentEditableExpression(attribute.value, scopes, /* @__PURE__ */ new Set());
3078
+ if (isNodeOfType(attribute.value, "JSXExpressionContainer")) return resolveContentEditableExpression(attribute.value.expression, scopes, /* @__PURE__ */ new Set());
3079
+ return UNKNOWN_CONTENT_EDITABLE;
3080
+ };
3081
+ const hasDefinitelyEnabledContentEditableAncestor = (node, scopes, settings) => {
3082
+ let ancestor = node.parent?.parent;
3083
+ while (ancestor) {
3084
+ if (isNodeOfType(ancestor, "JSXElement")) {
3085
+ const openingElement = ancestor.openingElement;
3086
+ if (!HTML_TAGS.has(getElementType(openingElement, settings))) return false;
3087
+ const possibilities = getContentEditablePossibilities(openingElement, scopes);
3088
+ if (possibilities) {
3089
+ if (possibilities.canBeEnabled && !possibilities.canBeDisabled && !possibilities.canBeInherited) return true;
3090
+ if (!(possibilities.canBeInherited && !possibilities.canBeDisabled && !possibilities.canBeEnabled)) return false;
3091
+ }
3092
+ }
3093
+ ancestor = ancestor.parent;
3094
+ }
3095
+ return false;
3096
+ };
3097
+ const canContentEditableBeTabbable = (node, scopes, settings) => {
3098
+ if (!getContentEditablePossibilities(node, scopes)?.canBeEnabled) return false;
3099
+ return !hasDefinitelyEnabledContentEditableAncestor(node, scopes, settings);
3100
+ };
3101
+ //#endregion
2740
3102
  //#region src/plugin/utils/is-interactive-element.ts
2741
3103
  const isInteractiveElement = (tagName, openingElement) => {
2742
3104
  switch (tagName) {
@@ -2809,19 +3171,6 @@ const parseJsxValue = (value) => {
2809
3171
  //#endregion
2810
3172
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
2811
3173
  const MESSAGE$61 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2812
- const mayBeContentEditable = (node) => {
2813
- const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
2814
- if (!attribute) return false;
2815
- if (!attribute.value) return true;
2816
- const stringValue = getJsxPropStringValue(attribute);
2817
- if (stringValue !== null) return stringValue !== "false";
2818
- const value = attribute.value;
2819
- if (isNodeOfType(value, "JSXExpressionContainer")) {
2820
- const expression = value.expression;
2821
- if (isNodeOfType(expression, "Literal")) return expression.value !== false && expression.value !== "false";
2822
- }
2823
- return true;
2824
- };
2825
3174
  const ariaActivedescendantHasTabindex = defineRule({
2826
3175
  id: "aria-activedescendant-has-tabindex",
2827
3176
  title: "aria-activedescendant missing tabindex",
@@ -2844,7 +3193,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2844
3193
  return;
2845
3194
  }
2846
3195
  if (isInteractiveElement(tag, node)) return;
2847
- if (mayBeContentEditable(node)) return;
3196
+ if (canContentEditableBeTabbable(node, context.scopes, context.settings)) return;
2848
3197
  context.report({
2849
3198
  node: node.name,
2850
3199
  message: MESSAGE$61
@@ -3297,227 +3646,6 @@ const ariaProptypes = defineRule({
3297
3646
  } })
3298
3647
  });
3299
3648
  //#endregion
3300
- //#region src/plugin/constants/aria-roles.ts
3301
- const VALID_ARIA_ROLES = new Set([
3302
- "alert",
3303
- "alertdialog",
3304
- "application",
3305
- "article",
3306
- "banner",
3307
- "blockquote",
3308
- "button",
3309
- "caption",
3310
- "cell",
3311
- "checkbox",
3312
- "code",
3313
- "columnheader",
3314
- "combobox",
3315
- "complementary",
3316
- "contentinfo",
3317
- "definition",
3318
- "deletion",
3319
- "dialog",
3320
- "directory",
3321
- "doc-abstract",
3322
- "doc-acknowledgments",
3323
- "doc-afterword",
3324
- "doc-appendix",
3325
- "doc-backlink",
3326
- "doc-biblioentry",
3327
- "doc-bibliography",
3328
- "doc-biblioref",
3329
- "doc-chapter",
3330
- "doc-colophon",
3331
- "doc-conclusion",
3332
- "doc-cover",
3333
- "doc-credit",
3334
- "doc-credits",
3335
- "doc-dedication",
3336
- "doc-endnote",
3337
- "doc-endnotes",
3338
- "doc-epigraph",
3339
- "doc-epilogue",
3340
- "doc-errata",
3341
- "doc-example",
3342
- "doc-footnote",
3343
- "doc-foreword",
3344
- "doc-glossary",
3345
- "doc-glossref",
3346
- "doc-index",
3347
- "doc-introduction",
3348
- "doc-noteref",
3349
- "doc-notice",
3350
- "doc-pagebreak",
3351
- "doc-pagelist",
3352
- "doc-part",
3353
- "doc-preface",
3354
- "doc-prologue",
3355
- "doc-pullquote",
3356
- "doc-qna",
3357
- "doc-subtitle",
3358
- "doc-tip",
3359
- "doc-toc",
3360
- "document",
3361
- "emphasis",
3362
- "feed",
3363
- "figure",
3364
- "form",
3365
- "generic",
3366
- "graphics-document",
3367
- "graphics-object",
3368
- "graphics-symbol",
3369
- "grid",
3370
- "gridcell",
3371
- "group",
3372
- "heading",
3373
- "img",
3374
- "insertion",
3375
- "link",
3376
- "list",
3377
- "listbox",
3378
- "listitem",
3379
- "log",
3380
- "main",
3381
- "mark",
3382
- "marquee",
3383
- "math",
3384
- "menu",
3385
- "menubar",
3386
- "menuitem",
3387
- "menuitemcheckbox",
3388
- "menuitemradio",
3389
- "meter",
3390
- "navigation",
3391
- "none",
3392
- "note",
3393
- "option",
3394
- "paragraph",
3395
- "presentation",
3396
- "progressbar",
3397
- "radio",
3398
- "radiogroup",
3399
- "region",
3400
- "row",
3401
- "rowgroup",
3402
- "rowheader",
3403
- "scrollbar",
3404
- "search",
3405
- "searchbox",
3406
- "separator",
3407
- "slider",
3408
- "spinbutton",
3409
- "status",
3410
- "strong",
3411
- "subscript",
3412
- "superscript",
3413
- "switch",
3414
- "tab",
3415
- "table",
3416
- "tablist",
3417
- "tabpanel",
3418
- "term",
3419
- "textbox",
3420
- "time",
3421
- "timer",
3422
- "toolbar",
3423
- "tooltip",
3424
- "tree",
3425
- "treegrid",
3426
- "treeitem"
3427
- ]);
3428
- const INTERACTIVE_ROLES = new Set([
3429
- "button",
3430
- "checkbox",
3431
- "columnheader",
3432
- "combobox",
3433
- "grid",
3434
- "gridcell",
3435
- "link",
3436
- "listbox",
3437
- "menu",
3438
- "menubar",
3439
- "menuitem",
3440
- "menuitemcheckbox",
3441
- "menuitemradio",
3442
- "option",
3443
- "radio",
3444
- "radiogroup",
3445
- "row",
3446
- "rowheader",
3447
- "scrollbar",
3448
- "searchbox",
3449
- "separator",
3450
- "slider",
3451
- "spinbutton",
3452
- "switch",
3453
- "tab",
3454
- "tablist",
3455
- "textbox",
3456
- "toolbar",
3457
- "tree",
3458
- "treegrid",
3459
- "treeitem"
3460
- ]);
3461
- const NON_INTERACTIVE_ROLES = new Set([
3462
- "alert",
3463
- "alertdialog",
3464
- "application",
3465
- "article",
3466
- "banner",
3467
- "blockquote",
3468
- "caption",
3469
- "cell",
3470
- "complementary",
3471
- "contentinfo",
3472
- "definition",
3473
- "deletion",
3474
- "dialog",
3475
- "directory",
3476
- "document",
3477
- "feed",
3478
- "figure",
3479
- "form",
3480
- "group",
3481
- "heading",
3482
- "img",
3483
- "insertion",
3484
- "list",
3485
- "listitem",
3486
- "log",
3487
- "main",
3488
- "marquee",
3489
- "math",
3490
- "navigation",
3491
- "note",
3492
- "paragraph",
3493
- "progressbar",
3494
- "region",
3495
- "rowgroup",
3496
- "search",
3497
- "status",
3498
- "table",
3499
- "tabpanel",
3500
- "term",
3501
- "time",
3502
- "timer",
3503
- "tooltip"
3504
- ]);
3505
- const ABSTRACT_ROLES = new Set([
3506
- "command",
3507
- "composite",
3508
- "input",
3509
- "landmark",
3510
- "range",
3511
- "roletype",
3512
- "section",
3513
- "sectionhead",
3514
- "select",
3515
- "structure",
3516
- "widget",
3517
- "window"
3518
- ]);
3519
- const PRESENTATION_ROLES$1 = new Set(["presentation", "none"]);
3520
- //#endregion
3521
3649
  //#region src/plugin/rules/a11y/aria-role.ts
3522
3650
  const buildBaseMessage = (suffix) => `This \`role\` is not a valid ARIA role, so assistive tech cannot expose it correctly. Use a real, non-abstract role.${suffix}`;
3523
3651
  const resolveSettings$50 = (settings) => {
@@ -5187,133 +5315,644 @@ const asyncAwaitInLoop = defineRule({
5187
5315
  }
5188
5316
  });
5189
5317
  //#endregion
5190
- //#region src/plugin/utils/collect-pattern-default-reference-names.ts
5191
- const collectPatternDefaultReferenceNames = (pattern, into) => {
5192
- if (!pattern) return;
5193
- if (isNodeOfType(pattern, "AssignmentPattern")) {
5194
- collectReferenceIdentifierNames(pattern.right, into);
5195
- collectPatternDefaultReferenceNames(pattern.left, into);
5318
+ //#region src/plugin/semantic/scope-analysis.ts
5319
+ const isHoistedBindingKind = (kind) => kind === "var" || kind === "function";
5320
+ const findHoistTargetScope = (scope) => {
5321
+ let current = scope;
5322
+ while (current) {
5323
+ if (current.kind === "module" || current.kind === "function" || current.kind === "arrow-function" || current.kind === "method") return current;
5324
+ current = current.parent;
5325
+ }
5326
+ return scope;
5327
+ };
5328
+ const createScope = (kind, node, parent, state) => {
5329
+ const scope = {
5330
+ id: state.nextScopeId++,
5331
+ kind,
5332
+ node,
5333
+ parent,
5334
+ children: [],
5335
+ symbols: [],
5336
+ references: [],
5337
+ symbolsByName: /* @__PURE__ */ new Map()
5338
+ };
5339
+ if (parent) parent.children.push(scope);
5340
+ return scope;
5341
+ };
5342
+ const pushScope = (kind, node, state) => {
5343
+ const scope = createScope(kind, node, state.currentScope, state);
5344
+ state.scopeStack.push(scope);
5345
+ state.currentScope = scope;
5346
+ state.ownScopeForNode.set(node, scope);
5347
+ return scope;
5348
+ };
5349
+ const popScope = (state) => {
5350
+ state.scopeStack.pop();
5351
+ const previous = state.scopeStack[state.scopeStack.length - 1];
5352
+ if (!previous) throw new Error("scope stack underflow");
5353
+ state.currentScope = previous;
5354
+ };
5355
+ const recordSymbol = (scope, state, options) => {
5356
+ const symbol = {
5357
+ id: state.nextSymbolId++,
5358
+ name: options.name,
5359
+ kind: options.kind,
5360
+ bindingIdentifier: options.bindingIdentifier,
5361
+ declarationNode: options.declarationNode,
5362
+ scope,
5363
+ initializer: options.initializer,
5364
+ references: []
5365
+ };
5366
+ scope.symbols.push(symbol);
5367
+ scope.symbolsByName.set(options.name, symbol);
5368
+ state.symbolByBindingIdentifier.set(options.bindingIdentifier, symbol);
5369
+ return symbol;
5370
+ };
5371
+ const collectBindingNamesFromPattern = (pattern) => {
5372
+ const out = [];
5373
+ const visit = (node) => {
5374
+ if (isNodeOfType(node, "Identifier")) {
5375
+ out.push(node);
5376
+ return;
5377
+ }
5378
+ if (isNodeOfType(node, "ObjectPattern")) {
5379
+ for (const property of node.properties) if (isNodeOfType(property, "Property")) {
5380
+ const propValue = property.value;
5381
+ visit(propValue);
5382
+ } else if (isNodeOfType(property, "RestElement")) visit(property.argument);
5383
+ return;
5384
+ }
5385
+ if (isNodeOfType(node, "ArrayPattern")) {
5386
+ for (const element of node.elements) if (element) visit(element);
5387
+ return;
5388
+ }
5389
+ if (isNodeOfType(node, "RestElement")) {
5390
+ visit(node.argument);
5391
+ return;
5392
+ }
5393
+ if (isNodeOfType(node, "AssignmentPattern")) visit(node.left);
5394
+ };
5395
+ visit(pattern);
5396
+ return out;
5397
+ };
5398
+ const visitDestructuringDeclarations = (pattern, baseInitializer, scope, state, symbolKind, declarationNode) => {
5399
+ if (isNodeOfType(pattern, "Identifier")) {
5400
+ recordSymbol(scope, state, {
5401
+ name: pattern.name,
5402
+ kind: symbolKind,
5403
+ bindingIdentifier: pattern,
5404
+ declarationNode,
5405
+ initializer: baseInitializer
5406
+ });
5196
5407
  return;
5197
5408
  }
5198
- if (isNodeOfType(pattern, "RestElement")) {
5199
- collectPatternDefaultReferenceNames(pattern.argument, into);
5409
+ if (isNodeOfType(pattern, "ObjectPattern")) {
5410
+ for (const property of pattern.properties) if (isNodeOfType(property, "Property")) {
5411
+ const propertyValue = property.value;
5412
+ visitDestructuringDeclarations(propertyValue, isNodeOfType(propertyValue, "AssignmentPattern") ? propertyValue.right : baseInitializer, scope, state, symbolKind, declarationNode);
5413
+ } else if (isNodeOfType(property, "RestElement")) visitDestructuringDeclarations(property.argument, baseInitializer, scope, state, symbolKind, declarationNode);
5200
5414
  return;
5201
5415
  }
5202
5416
  if (isNodeOfType(pattern, "ArrayPattern")) {
5203
- for (const element of pattern.elements ?? []) collectPatternDefaultReferenceNames(element, into);
5417
+ for (const element of pattern.elements) {
5418
+ if (!element) continue;
5419
+ visitDestructuringDeclarations(element, isNodeOfType(element, "AssignmentPattern") ? element.right ?? null : baseInitializer, scope, state, symbolKind, declarationNode);
5420
+ }
5204
5421
  return;
5205
5422
  }
5206
- if (isNodeOfType(pattern, "ObjectPattern")) {
5207
- for (const property of pattern.properties ?? []) if (isNodeOfType(property, "RestElement")) collectPatternDefaultReferenceNames(property.argument, into);
5208
- else if (isNodeOfType(property, "Property")) {
5209
- if (property.computed) collectReferenceIdentifierNames(property.key, into);
5210
- collectPatternDefaultReferenceNames(property.value, into);
5211
- }
5423
+ if (isNodeOfType(pattern, "AssignmentPattern")) {
5424
+ visitDestructuringDeclarations(pattern.left, pattern.right ?? null, scope, state, symbolKind, declarationNode);
5425
+ return;
5212
5426
  }
5427
+ if (isNodeOfType(pattern, "RestElement")) visitDestructuringDeclarations(pattern.argument, null, scope, state, symbolKind, declarationNode);
5213
5428
  };
5214
- //#endregion
5215
- //#region src/plugin/utils/is-bare-await-expression-statement.ts
5216
- const isBareAwaitExpressionStatement = (statement) => {
5217
- if (!isNodeOfType(statement, "ExpressionStatement")) return false;
5218
- return isNodeOfType(statement.expression, "AwaitExpression");
5429
+ const tagAsBinding = (state, identifier) => {
5430
+ bindingPositionMarker.add(identifier);
5219
5431
  };
5220
- //#endregion
5221
- //#region src/plugin/utils/is-early-exit-if-statement.ts
5222
- const isEarlyExitStatement = (statement) => isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement") || isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
5223
- const isEarlyExitIfStatement = (statement) => {
5224
- if (!isNodeOfType(statement, "IfStatement")) return false;
5225
- const consequent = statement.consequent;
5226
- if (!consequent) return false;
5227
- if (isEarlyExitStatement(consequent)) return true;
5228
- if (!isNodeOfType(consequent, "BlockStatement")) return false;
5229
- for (const inner of consequent.body ?? []) if (isEarlyExitStatement(inner)) return true;
5230
- return false;
5432
+ const bindingPositionMarker = /* @__PURE__ */ new WeakSet();
5433
+ const recordReference = (state, identifier, flag) => {
5434
+ const reference = {
5435
+ id: state.nextReferenceId++,
5436
+ identifier,
5437
+ resolvedSymbol: null,
5438
+ flag,
5439
+ scope: state.currentScope
5440
+ };
5441
+ state.currentScope.references.push(reference);
5442
+ state.referenceByIdentifier.set(identifier, reference);
5231
5443
  };
5232
- //#endregion
5233
- //#region src/plugin/rules/performance/async-defer-await.ts
5234
- const hasAnyIdentifierName = (identifierNames, candidateNames) => {
5235
- for (const candidateName of candidateNames) if (identifierNames.has(candidateName)) return true;
5236
- return false;
5444
+ const isFunctionBodyBlock = (block) => {
5445
+ if (!block.parent) return false;
5446
+ return isFunctionLike$1(block.parent);
5237
5447
  };
5238
- const collectDeclaratorDependencyIdentifierNames = (declarator, into) => {
5239
- if (!isNodeOfType(declarator, "VariableDeclarator")) return;
5240
- collectReferenceIdentifierNames(declarator.init, into);
5241
- collectPatternDefaultReferenceNames(declarator.id, into);
5448
+ const isCatchClauseBlock = (block) => block.parent !== null && block.parent !== void 0 && block.parent.type === "CatchClause";
5449
+ const handleVariableDeclaration = (declaration, state) => {
5450
+ if (!isNodeOfType(declaration, "VariableDeclaration")) return;
5451
+ const symbolKind = declaration.kind === "var" ? "var" : declaration.kind === "let" ? "let" : declaration.kind === "const" ? "const" : "using";
5452
+ const targetScope = isHoistedBindingKind(symbolKind) ? findHoistTargetScope(state.currentScope) : state.currentScope;
5453
+ for (const declarator of declaration.declarations) {
5454
+ const declaratorNode = declarator;
5455
+ const init = declarator.init ?? null;
5456
+ visitDestructuringDeclarations(declarator.id, init, targetScope, state, symbolKind, declaratorNode);
5457
+ for (const identifier of collectBindingNamesFromPattern(declarator.id)) tagAsBinding(state, identifier);
5458
+ }
5242
5459
  };
5243
- const processVariableDeclaration = (declaration, awaitedBindingNames) => {
5244
- if (!isNodeOfType(declaration, "VariableDeclaration")) return {
5245
- didIntroduceAwait: false,
5246
- didGrowBindings: false
5247
- };
5248
- let didIntroduceAwait = false;
5249
- const sizeBeforeAll = awaitedBindingNames.size;
5250
- let hasChanged = true;
5251
- while (hasChanged) {
5252
- hasChanged = false;
5253
- for (const declarator of declaration.declarations ?? []) {
5254
- if (!isNodeOfType(declarator, "VariableDeclarator")) continue;
5255
- if (containsDirectAwait(declarator.init) || containsDirectAwait(declarator.id)) {
5256
- didIntroduceAwait = true;
5257
- const sizeBefore = awaitedBindingNames.size;
5258
- collectPatternNames(declarator.id, awaitedBindingNames);
5259
- if (awaitedBindingNames.size > sizeBefore) hasChanged = true;
5260
- continue;
5261
- }
5262
- const dependencyIdentifiers = /* @__PURE__ */ new Set();
5263
- collectDeclaratorDependencyIdentifierNames(declarator, dependencyIdentifiers);
5264
- if (!hasAnyIdentifierName(dependencyIdentifiers, awaitedBindingNames)) continue;
5265
- const sizeBefore = awaitedBindingNames.size;
5266
- collectPatternNames(declarator.id, awaitedBindingNames);
5267
- if (awaitedBindingNames.size > sizeBefore) hasChanged = true;
5268
- }
5460
+ const handleFunctionDeclaration = (fn, state) => {
5461
+ if (!isNodeOfType(fn, "FunctionDeclaration")) return;
5462
+ if (fn.id) {
5463
+ recordSymbol(findHoistTargetScope(state.currentScope), state, {
5464
+ name: fn.id.name,
5465
+ kind: "function",
5466
+ bindingIdentifier: fn.id,
5467
+ declarationNode: fn,
5468
+ initializer: fn
5469
+ });
5470
+ tagAsBinding(state, fn.id);
5269
5471
  }
5270
- const didGrowBindings = awaitedBindingNames.size > sizeBeforeAll;
5271
- return {
5272
- didIntroduceAwait,
5273
- didGrowBindings
5274
- };
5275
5472
  };
5276
- const CANCELLATION_GUARD_NAMES = new Set([
5277
- "cancelled",
5278
- "canceled",
5279
- "isCancelled",
5280
- "isCanceled",
5281
- "aborted",
5282
- "isAborted",
5283
- "disposed",
5284
- "isDisposed",
5285
- "destroyed",
5286
- "isDestroyed",
5287
- "stopped",
5288
- "isStopped",
5289
- "mounted",
5290
- "isMounted",
5291
- "unmounted",
5292
- "isUnmounted",
5293
- "active",
5294
- "isActive",
5295
- "stale",
5296
- "isStale",
5297
- "signal",
5298
- "abortSignal",
5299
- "abortController"
5300
- ]);
5301
- const testReadsRefCurrent = (test) => {
5302
- let didFindRefCurrentRead = false;
5303
- walkAst(test, (child) => {
5304
- if (didFindRefCurrentRead) return false;
5305
- if (!isNodeOfType(child, "MemberExpression") || child.computed) return;
5306
- if (!isNodeOfType(child.property, "Identifier") || child.property.name !== "current") return;
5307
- if (!isNodeOfType(child.object, "Identifier")) return;
5308
- const refCandidateName = child.object.name;
5309
- if (refCandidateName.endsWith("Ref") && refCandidateName.length > 3) {
5310
- didFindRefCurrentRead = true;
5311
- return false;
5312
- }
5473
+ const handleClassDeclaration = (cls, state) => {
5474
+ if (!isNodeOfType(cls, "ClassDeclaration")) return;
5475
+ if (cls.id) {
5476
+ recordSymbol(state.currentScope, state, {
5477
+ name: cls.id.name,
5478
+ kind: "class",
5479
+ bindingIdentifier: cls.id,
5480
+ declarationNode: cls,
5481
+ initializer: cls
5482
+ });
5483
+ tagAsBinding(state, cls.id);
5484
+ }
5485
+ };
5486
+ const handleImportDeclaration = (importDeclaration, state) => {
5487
+ if (!isNodeOfType(importDeclaration, "ImportDeclaration")) return;
5488
+ const target = findHoistTargetScope(state.currentScope);
5489
+ for (const specifier of importDeclaration.specifiers) {
5490
+ const local = specifier.local;
5491
+ if (!isNodeOfType(local, "Identifier")) continue;
5492
+ recordSymbol(target, state, {
5493
+ name: local.name,
5494
+ kind: "import",
5495
+ bindingIdentifier: local,
5496
+ declarationNode: specifier,
5497
+ initializer: specifier
5498
+ });
5499
+ tagAsBinding(state, local);
5500
+ }
5501
+ };
5502
+ const handleTsDeclarations = (node, state) => {
5503
+ if (node.type !== "TSImportEqualsDeclaration" && node.type !== "TSEnumDeclaration" && node.type !== "TSTypeAliasDeclaration" && node.type !== "TSInterfaceDeclaration" && node.type !== "TSModuleDeclaration") return;
5504
+ const idNode = node.id;
5505
+ if (!idNode || !isNodeOfType(idNode, "Identifier")) return;
5506
+ const kind = node.type === "TSImportEqualsDeclaration" ? "ts-import-equals" : node.type === "TSEnumDeclaration" ? "ts-enum" : node.type === "TSTypeAliasDeclaration" ? "ts-type-alias" : node.type === "TSInterfaceDeclaration" ? "ts-interface" : "ts-module";
5507
+ recordSymbol(findHoistTargetScope(state.currentScope), state, {
5508
+ name: idNode.name,
5509
+ kind,
5510
+ bindingIdentifier: idNode,
5511
+ declarationNode: node,
5512
+ initializer: null
5313
5513
  });
5314
- return didFindRefCurrentRead;
5514
+ tagAsBinding(state, idNode);
5315
5515
  };
5316
- const CANCELLATION_NAME_FRAGMENTS = [
5516
+ const handleFunctionParameters = (params, scope, state) => {
5517
+ for (const param of params) {
5518
+ visitDestructuringDeclarations(param, null, scope, state, "parameter", param);
5519
+ for (const identifier of collectBindingNamesFromPattern(param)) tagAsBinding(state, identifier);
5520
+ }
5521
+ };
5522
+ const shouldPushBlockScope = (block) => {
5523
+ if (!isNodeOfType(block, "BlockStatement")) return false;
5524
+ if (isFunctionBodyBlock(block)) return false;
5525
+ if (isCatchClauseBlock(block)) return false;
5526
+ if (block.parent && block.parent.type === "TSModuleDeclaration") return false;
5527
+ return true;
5528
+ };
5529
+ const isJsxIdentifierBindingReference = (identifier) => {
5530
+ const parent = identifier.parent;
5531
+ if (!parent) return false;
5532
+ if (parent.type === "JSXMemberExpression") return parent.object === identifier;
5533
+ if (parent.type === "JSXNamespacedName") return false;
5534
+ const ASCII_LOWERCASE_A = 97;
5535
+ const ASCII_LOWERCASE_Z = 122;
5536
+ const firstCharCode = identifier.name.charCodeAt(0);
5537
+ if (firstCharCode >= ASCII_LOWERCASE_A && firstCharCode <= ASCII_LOWERCASE_Z) return false;
5538
+ return true;
5539
+ };
5540
+ const isNonReferencePosition = (identifier) => {
5541
+ const parent = identifier.parent;
5542
+ if (!parent) return false;
5543
+ switch (parent.type) {
5544
+ case "MemberExpression": return parent.property === identifier && !parent.computed;
5545
+ case "Property": return parent.key === identifier && !parent.computed && !parent.shorthand;
5546
+ case "MethodDefinition":
5547
+ case "PropertyDefinition": return parent.key === identifier && !parent.computed;
5548
+ case "JSXAttribute": return parent.name === identifier;
5549
+ case "ImportSpecifier": return parent.imported === identifier;
5550
+ case "ExportSpecifier": return parent.exported === identifier && parent.local !== parent.exported;
5551
+ case "LabeledStatement":
5552
+ case "BreakStatement":
5553
+ case "ContinueStatement": return parent.label === identifier;
5554
+ default: return false;
5555
+ }
5556
+ };
5557
+ const inferReferenceFlag = (identifier) => {
5558
+ const parent = identifier.parent;
5559
+ if (!parent) return "read";
5560
+ switch (parent.type) {
5561
+ case "AssignmentExpression":
5562
+ if (parent.left === identifier) return parent.operator === "=" ? "write" : "read-write";
5563
+ return "read";
5564
+ case "UpdateExpression": return "read-write";
5565
+ case "ForInStatement":
5566
+ case "ForOfStatement": return parent.left === identifier ? "write" : "read";
5567
+ default: return "read";
5568
+ }
5569
+ };
5570
+ const setNodeScope = (node, state) => {
5571
+ state.nodeScope.set(node, state.currentScope);
5572
+ };
5573
+ const walkParameterReferences = (pattern, state) => {
5574
+ if (isNodeOfType(pattern, "AssignmentPattern")) {
5575
+ walkParameterReferences(pattern.left, state);
5576
+ const defaultValue = pattern.right ?? null;
5577
+ if (defaultValue) walk(defaultValue, state);
5578
+ return;
5579
+ }
5580
+ if (isNodeOfType(pattern, "ObjectPattern")) {
5581
+ for (const property of pattern.properties) {
5582
+ const propertyNode = property;
5583
+ if (isNodeOfType(propertyNode, "RestElement")) {
5584
+ walkParameterReferences(propertyNode.argument, state);
5585
+ continue;
5586
+ }
5587
+ if (!isNodeOfType(propertyNode, "Property")) continue;
5588
+ const propertyDetail = propertyNode;
5589
+ if (propertyDetail.computed) walk(propertyDetail.key, state);
5590
+ walkParameterReferences(propertyDetail.value, state);
5591
+ }
5592
+ return;
5593
+ }
5594
+ if (isNodeOfType(pattern, "ArrayPattern")) {
5595
+ for (const element of pattern.elements) if (element) walkParameterReferences(element, state);
5596
+ return;
5597
+ }
5598
+ if (isNodeOfType(pattern, "RestElement")) walkParameterReferences(pattern.argument, state);
5599
+ };
5600
+ const walk = (node, state) => {
5601
+ if (isFunctionLike$1(node)) {
5602
+ if (isNodeOfType(node, "FunctionDeclaration") && node.id) handleFunctionDeclaration(node, state);
5603
+ const functionParams = node.params ?? [];
5604
+ for (const param of functionParams) {
5605
+ if (!("decorators" in param) || !Array.isArray(param.decorators)) continue;
5606
+ for (const decorator of param.decorators) if (isAstNode(decorator)) walk(decorator, state);
5607
+ }
5608
+ setNodeScope(node, state);
5609
+ const fnScope = pushScope(node.type === "ArrowFunctionExpression" ? "arrow-function" : "function", node, state);
5610
+ if ((isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "FunctionDeclaration")) && node.id) {
5611
+ recordSymbol(fnScope, state, {
5612
+ name: node.id.name,
5613
+ kind: "function",
5614
+ bindingIdentifier: node.id,
5615
+ declarationNode: node,
5616
+ initializer: node
5617
+ });
5618
+ tagAsBinding(state, node.id);
5619
+ }
5620
+ handleFunctionParameters(functionParams, fnScope, state);
5621
+ for (const param of functionParams) walkParameterReferences(param, state);
5622
+ const body = node.body;
5623
+ if (body) walk(body, state);
5624
+ popScope(state);
5625
+ return;
5626
+ }
5627
+ if (isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression")) {
5628
+ if (isNodeOfType(node, "ClassDeclaration") && node.id) handleClassDeclaration(node, state);
5629
+ if (Array.isArray(node.decorators)) {
5630
+ for (const decorator of node.decorators) if (isAstNode(decorator)) walk(decorator, state);
5631
+ }
5632
+ const classScope = pushScope("class", node, state);
5633
+ setNodeScope(node, state);
5634
+ if (isNodeOfType(node, "ClassExpression") && node.id) {
5635
+ recordSymbol(classScope, state, {
5636
+ name: node.id.name,
5637
+ kind: "class",
5638
+ bindingIdentifier: node.id,
5639
+ declarationNode: node,
5640
+ initializer: node
5641
+ });
5642
+ tagAsBinding(state, node.id);
5643
+ }
5644
+ if (node.superClass) walk(node.superClass, state);
5645
+ if (node.body) walk(node.body, state);
5646
+ popScope(state);
5647
+ return;
5648
+ }
5649
+ if (isNodeOfType(node, "CatchClause")) {
5650
+ const catchScope = pushScope("catch", node, state);
5651
+ setNodeScope(node, state);
5652
+ if (node.param) {
5653
+ visitDestructuringDeclarations(node.param, null, catchScope, state, "catch-clause-parameter", node);
5654
+ for (const identifier of collectBindingNamesFromPattern(node.param)) tagAsBinding(state, identifier);
5655
+ }
5656
+ if (node.body) walk(node.body, state);
5657
+ popScope(state);
5658
+ return;
5659
+ }
5660
+ if (isNodeOfType(node, "ForStatement") || isNodeOfType(node, "ForInStatement") || isNodeOfType(node, "ForOfStatement")) {
5661
+ pushScope("for", node, state);
5662
+ setNodeScope(node, state);
5663
+ const nodeRecord = node;
5664
+ for (const key of Object.keys(nodeRecord)) {
5665
+ if (key === "parent") continue;
5666
+ if (TYPE_POSITION_CHILD_KEYS.has(key)) continue;
5667
+ const child = nodeRecord[key];
5668
+ if (Array.isArray(child)) {
5669
+ for (const item of child) if (isAstNode(item)) walk(item, state);
5670
+ } else if (isAstNode(child)) walk(child, state);
5671
+ }
5672
+ popScope(state);
5673
+ return;
5674
+ }
5675
+ if (isNodeOfType(node, "SwitchStatement")) {
5676
+ pushScope("switch", node, state);
5677
+ setNodeScope(node, state);
5678
+ if (node.discriminant) walk(node.discriminant, state);
5679
+ for (const switchCase of node.cases) walk(switchCase, state);
5680
+ popScope(state);
5681
+ return;
5682
+ }
5683
+ if (isNodeOfType(node, "TSModuleDeclaration")) {
5684
+ const moduleScope = pushScope("ts-module", node, state);
5685
+ setNodeScope(node, state);
5686
+ if (node.id && isNodeOfType(node.id, "Identifier")) {
5687
+ const identifier = node.id;
5688
+ recordSymbol(findHoistTargetScope(moduleScope.parent ?? state.currentScope), state, {
5689
+ name: identifier.name,
5690
+ kind: "ts-module",
5691
+ bindingIdentifier: identifier,
5692
+ declarationNode: node,
5693
+ initializer: null
5694
+ });
5695
+ tagAsBinding(state, identifier);
5696
+ }
5697
+ if (node.body) walk(node.body, state);
5698
+ popScope(state);
5699
+ return;
5700
+ }
5701
+ if (isNodeOfType(node, "TSEnumDeclaration")) {
5702
+ handleTsDeclarations(node, state);
5703
+ pushScope("ts-enum", node, state);
5704
+ setNodeScope(node, state);
5705
+ const members = node.members ?? [];
5706
+ for (const member of members) walk(member, state);
5707
+ popScope(state);
5708
+ return;
5709
+ }
5710
+ if (isNodeOfType(node, "BlockStatement") && shouldPushBlockScope(node)) {
5711
+ pushScope("block", node, state);
5712
+ setNodeScope(node, state);
5713
+ for (const statement of node.body) walk(statement, state);
5714
+ popScope(state);
5715
+ return;
5716
+ }
5717
+ setNodeScope(node, state);
5718
+ if (isNodeOfType(node, "VariableDeclaration")) handleVariableDeclaration(node, state);
5719
+ else if (isNodeOfType(node, "FunctionDeclaration")) handleFunctionDeclaration(node, state);
5720
+ else if (isNodeOfType(node, "ClassDeclaration")) handleClassDeclaration(node, state);
5721
+ else if (isNodeOfType(node, "ImportDeclaration")) handleImportDeclaration(node, state);
5722
+ else if (node.type === "TSImportEqualsDeclaration" || node.type === "TSTypeAliasDeclaration" || node.type === "TSInterfaceDeclaration") handleTsDeclarations(node, state);
5723
+ if ((isNodeOfType(node, "Identifier") || isNodeOfType(node, "JSXIdentifier")) && !bindingPositionMarker.has(node) && !isNonReferencePosition(node)) if (isNodeOfType(node, "JSXIdentifier")) {
5724
+ if (isJsxIdentifierBindingReference(node)) recordReference(state, node, inferReferenceFlag(node));
5725
+ } else recordReference(state, node, inferReferenceFlag(node));
5726
+ const nodeRecord = node;
5727
+ for (const key of Object.keys(nodeRecord)) {
5728
+ if (key === "parent") continue;
5729
+ if (TYPE_POSITION_CHILD_KEYS.has(key)) continue;
5730
+ const child = nodeRecord[key];
5731
+ if (Array.isArray(child)) {
5732
+ for (const item of child) if (isAstNode(item)) walk(item, state);
5733
+ } else if (isAstNode(child)) walk(child, state);
5734
+ }
5735
+ };
5736
+ const resolveReferences = (rootScope) => {
5737
+ const visitScope = (scope) => {
5738
+ for (const reference of scope.references) {
5739
+ const name = reference.identifier.name;
5740
+ if (typeof name !== "string") continue;
5741
+ let lookup = scope;
5742
+ while (lookup) {
5743
+ const found = lookup.symbolsByName.get(name);
5744
+ if (found) {
5745
+ reference.resolvedSymbol = found;
5746
+ found.references.push(reference);
5747
+ break;
5748
+ }
5749
+ lookup = lookup.parent;
5750
+ }
5751
+ }
5752
+ for (const child of scope.children) visitScope(child);
5753
+ };
5754
+ visitScope(rootScope);
5755
+ };
5756
+ const analyzeScopes = (program) => {
5757
+ const rootScope = {
5758
+ id: 0,
5759
+ kind: "module",
5760
+ node: program,
5761
+ parent: null,
5762
+ children: [],
5763
+ symbols: [],
5764
+ references: [],
5765
+ symbolsByName: /* @__PURE__ */ new Map()
5766
+ };
5767
+ const state = {
5768
+ nextScopeId: 1,
5769
+ nextSymbolId: 0,
5770
+ nextReferenceId: 0,
5771
+ currentScope: rootScope,
5772
+ scopeStack: [rootScope],
5773
+ rootScope,
5774
+ nodeScope: /* @__PURE__ */ new WeakMap(),
5775
+ ownScopeForNode: /* @__PURE__ */ new WeakMap(),
5776
+ symbolByBindingIdentifier: /* @__PURE__ */ new WeakMap(),
5777
+ referenceByIdentifier: /* @__PURE__ */ new WeakMap()
5778
+ };
5779
+ state.nodeScope.set(program, rootScope);
5780
+ state.ownScopeForNode.set(program, rootScope);
5781
+ if (isNodeOfType(program, "Program")) for (const statement of program.body) walk(statement, state);
5782
+ else walk(program, state);
5783
+ resolveReferences(rootScope);
5784
+ const scopeFor = (node) => {
5785
+ let current = node;
5786
+ while (current) {
5787
+ const scope = state.nodeScope.get(current);
5788
+ if (scope) return scope;
5789
+ current = current.parent ?? null;
5790
+ }
5791
+ return rootScope;
5792
+ };
5793
+ const symbolFor = (identifier) => {
5794
+ const reference = state.referenceByIdentifier.get(identifier);
5795
+ if (reference) return reference.resolvedSymbol;
5796
+ const symbolForBinding = state.symbolByBindingIdentifier.get(identifier);
5797
+ if (symbolForBinding) return symbolForBinding;
5798
+ return null;
5799
+ };
5800
+ const referenceFor = (identifier) => {
5801
+ return state.referenceByIdentifier.get(identifier) ?? null;
5802
+ };
5803
+ const isGlobalReference = (identifier) => {
5804
+ const reference = state.referenceByIdentifier.get(identifier);
5805
+ if (!reference) return false;
5806
+ return reference.resolvedSymbol === null;
5807
+ };
5808
+ const ownScopeFor = (node) => {
5809
+ return state.ownScopeForNode.get(node) ?? null;
5810
+ };
5811
+ return {
5812
+ rootScope,
5813
+ scopeFor,
5814
+ ownScopeFor,
5815
+ symbolFor,
5816
+ referenceFor,
5817
+ isGlobalReference
5818
+ };
5819
+ };
5820
+ const isDescendantScope = (inner, outer) => {
5821
+ let current = inner;
5822
+ while (current) {
5823
+ if (current === outer) return true;
5824
+ current = current.parent;
5825
+ }
5826
+ return false;
5827
+ };
5828
+ //#endregion
5829
+ //#region src/plugin/utils/collect-pattern-default-reference-names.ts
5830
+ const collectPatternDefaultReferenceNames = (pattern, into) => {
5831
+ if (!pattern) return;
5832
+ if (isNodeOfType(pattern, "AssignmentPattern")) {
5833
+ collectReferenceIdentifierNames(pattern.right, into);
5834
+ collectPatternDefaultReferenceNames(pattern.left, into);
5835
+ return;
5836
+ }
5837
+ if (isNodeOfType(pattern, "RestElement")) {
5838
+ collectPatternDefaultReferenceNames(pattern.argument, into);
5839
+ return;
5840
+ }
5841
+ if (isNodeOfType(pattern, "ArrayPattern")) {
5842
+ for (const element of pattern.elements ?? []) collectPatternDefaultReferenceNames(element, into);
5843
+ return;
5844
+ }
5845
+ if (isNodeOfType(pattern, "ObjectPattern")) {
5846
+ for (const property of pattern.properties ?? []) if (isNodeOfType(property, "RestElement")) collectPatternDefaultReferenceNames(property.argument, into);
5847
+ else if (isNodeOfType(property, "Property")) {
5848
+ if (property.computed) collectReferenceIdentifierNames(property.key, into);
5849
+ collectPatternDefaultReferenceNames(property.value, into);
5850
+ }
5851
+ }
5852
+ };
5853
+ //#endregion
5854
+ //#region src/plugin/utils/is-bare-await-expression-statement.ts
5855
+ const isBareAwaitExpressionStatement = (statement) => {
5856
+ if (!isNodeOfType(statement, "ExpressionStatement")) return false;
5857
+ return isNodeOfType(statement.expression, "AwaitExpression");
5858
+ };
5859
+ //#endregion
5860
+ //#region src/plugin/utils/is-early-exit-if-statement.ts
5861
+ const isEarlyExitStatement = (statement) => isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement") || isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
5862
+ const isEarlyExitIfStatement = (statement) => {
5863
+ if (!isNodeOfType(statement, "IfStatement")) return false;
5864
+ const consequent = statement.consequent;
5865
+ if (!consequent) return false;
5866
+ if (isEarlyExitStatement(consequent)) return true;
5867
+ if (!isNodeOfType(consequent, "BlockStatement")) return false;
5868
+ for (const inner of consequent.body ?? []) if (isEarlyExitStatement(inner)) return true;
5869
+ return false;
5870
+ };
5871
+ //#endregion
5872
+ //#region src/plugin/rules/performance/async-defer-await.ts
5873
+ const hasAnyIdentifierName = (identifierNames, candidateNames) => {
5874
+ for (const candidateName of candidateNames) if (identifierNames.has(candidateName)) return true;
5875
+ return false;
5876
+ };
5877
+ const collectDeclaratorDependencyIdentifierNames = (declarator, into) => {
5878
+ if (!isNodeOfType(declarator, "VariableDeclarator")) return;
5879
+ collectReferenceIdentifierNames(declarator.init, into);
5880
+ collectPatternDefaultReferenceNames(declarator.id, into);
5881
+ };
5882
+ const processVariableDeclaration = (declaration, awaitedBindingNames) => {
5883
+ if (!isNodeOfType(declaration, "VariableDeclaration")) return {
5884
+ didIntroduceAwait: false,
5885
+ didGrowBindings: false
5886
+ };
5887
+ let didIntroduceAwait = false;
5888
+ const sizeBeforeAll = awaitedBindingNames.size;
5889
+ let hasChanged = true;
5890
+ while (hasChanged) {
5891
+ hasChanged = false;
5892
+ for (const declarator of declaration.declarations ?? []) {
5893
+ if (!isNodeOfType(declarator, "VariableDeclarator")) continue;
5894
+ if (containsDirectAwait(declarator.init) || containsDirectAwait(declarator.id)) {
5895
+ didIntroduceAwait = true;
5896
+ const sizeBefore = awaitedBindingNames.size;
5897
+ collectPatternNames(declarator.id, awaitedBindingNames);
5898
+ if (awaitedBindingNames.size > sizeBefore) hasChanged = true;
5899
+ continue;
5900
+ }
5901
+ const dependencyIdentifiers = /* @__PURE__ */ new Set();
5902
+ collectDeclaratorDependencyIdentifierNames(declarator, dependencyIdentifiers);
5903
+ if (!hasAnyIdentifierName(dependencyIdentifiers, awaitedBindingNames)) continue;
5904
+ const sizeBefore = awaitedBindingNames.size;
5905
+ collectPatternNames(declarator.id, awaitedBindingNames);
5906
+ if (awaitedBindingNames.size > sizeBefore) hasChanged = true;
5907
+ }
5908
+ }
5909
+ const didGrowBindings = awaitedBindingNames.size > sizeBeforeAll;
5910
+ return {
5911
+ didIntroduceAwait,
5912
+ didGrowBindings
5913
+ };
5914
+ };
5915
+ const CANCELLATION_GUARD_NAMES = new Set([
5916
+ "cancelled",
5917
+ "canceled",
5918
+ "isCancelled",
5919
+ "isCanceled",
5920
+ "aborted",
5921
+ "isAborted",
5922
+ "disposed",
5923
+ "isDisposed",
5924
+ "destroyed",
5925
+ "isDestroyed",
5926
+ "stopped",
5927
+ "isStopped",
5928
+ "mounted",
5929
+ "isMounted",
5930
+ "unmounted",
5931
+ "isUnmounted",
5932
+ "active",
5933
+ "isActive",
5934
+ "stale",
5935
+ "isStale",
5936
+ "signal",
5937
+ "abortSignal",
5938
+ "abortController"
5939
+ ]);
5940
+ const testReadsRefCurrent = (test) => {
5941
+ let didFindRefCurrentRead = false;
5942
+ walkAst(test, (child) => {
5943
+ if (didFindRefCurrentRead) return false;
5944
+ if (!isNodeOfType(child, "MemberExpression") || child.computed) return;
5945
+ if (!isNodeOfType(child.property, "Identifier") || child.property.name !== "current") return;
5946
+ if (!isNodeOfType(child.object, "Identifier")) return;
5947
+ const refCandidateName = child.object.name;
5948
+ if (refCandidateName.endsWith("Ref") && refCandidateName.length > 3) {
5949
+ didFindRefCurrentRead = true;
5950
+ return false;
5951
+ }
5952
+ });
5953
+ return didFindRefCurrentRead;
5954
+ };
5955
+ const CANCELLATION_NAME_FRAGMENTS = [
5317
5956
  "cancel",
5318
5957
  "abort",
5319
5958
  "dispos",
@@ -5371,15 +6010,56 @@ const guardTestReadsMutableEnvironment = (test) => {
5371
6010
  };
5372
6011
  const isNonLiteralComparisonTest = (test) => {
5373
6012
  if (!test) return false;
5374
- if (!isNodeOfType(test, "BinaryExpression")) return false;
6013
+ const unwrappedTest = stripParenExpression(test);
6014
+ if (!isNodeOfType(unwrappedTest, "BinaryExpression")) return false;
5375
6015
  if (![
5376
6016
  "===",
5377
6017
  "!==",
5378
6018
  "==",
5379
6019
  "!="
5380
- ].includes(test.operator)) return false;
5381
- const isLiteralOperand = (operand) => isNodeOfType(operand, "Literal") || isNodeOfType(operand, "TemplateLiteral") || isNodeOfType(operand, "UnaryExpression") && isLiteralOperand(operand.argument);
5382
- return !isLiteralOperand(test.left) && !isLiteralOperand(test.right);
6020
+ ].includes(unwrappedTest.operator)) return false;
6021
+ const isLiteralOperand = (operand) => {
6022
+ const unwrappedOperand = stripParenExpression(operand);
6023
+ return isNodeOfType(unwrappedOperand, "Literal") || isNodeOfType(unwrappedOperand, "TemplateLiteral") || isNodeOfType(unwrappedOperand, "UnaryExpression") && isLiteralOperand(unwrappedOperand.argument);
6024
+ };
6025
+ return !isLiteralOperand(unwrappedTest.left) && !isLiteralOperand(unwrappedTest.right);
6026
+ };
6027
+ const isLocalConstSnapshotOperand = (operand, scopes, functionScope) => {
6028
+ const unwrappedOperand = stripParenExpression(operand);
6029
+ if (!isNodeOfType(unwrappedOperand, "Identifier")) return false;
6030
+ const symbol = scopes.symbolFor(unwrappedOperand);
6031
+ return Boolean(symbol && symbol.kind === "const" && symbol.initializer && isDescendantScope(symbol.scope, functionScope));
6032
+ };
6033
+ const isLiveFreshnessOperand = (operand, scopes, functionScope) => {
6034
+ const unwrappedOperand = stripParenExpression(operand);
6035
+ if (isNodeOfType(unwrappedOperand, "MemberExpression")) return true;
6036
+ if (!isNodeOfType(unwrappedOperand, "Identifier")) return false;
6037
+ const symbol = scopes.symbolFor(unwrappedOperand);
6038
+ return Boolean(symbol && (symbol.kind === "let" || symbol.kind === "var" || symbol.kind === "import") && !isDescendantScope(symbol.scope, functionScope));
6039
+ };
6040
+ const isProvenFreshnessComparison = (test, scopes, functionScope) => {
6041
+ const unwrappedTest = stripParenExpression(test);
6042
+ if (!isNonLiteralComparisonTest(unwrappedTest)) return false;
6043
+ if (!isNodeOfType(unwrappedTest, "BinaryExpression")) return false;
6044
+ return isLocalConstSnapshotOperand(unwrappedTest.left, scopes, functionScope) && isLiveFreshnessOperand(unwrappedTest.right, scopes, functionScope) || isLocalConstSnapshotOperand(unwrappedTest.right, scopes, functionScope) && isLiveFreshnessOperand(unwrappedTest.left, scopes, functionScope);
6045
+ };
6046
+ const isLogicalCompositionOfFreshnessComparisons = (test, scopes, functionScope) => {
6047
+ if (!functionScope) return false;
6048
+ if (!test) return false;
6049
+ const unwrappedTest = stripParenExpression(test);
6050
+ if (!isNodeOfType(unwrappedTest, "LogicalExpression") || unwrappedTest.operator !== "&&" && unwrappedTest.operator !== "||") return false;
6051
+ const pendingTests = [unwrappedTest.left, unwrappedTest.right];
6052
+ while (pendingTests.length > 0) {
6053
+ const candidateTest = pendingTests.pop();
6054
+ if (!candidateTest) continue;
6055
+ const unwrappedCandidateTest = stripParenExpression(candidateTest);
6056
+ if (isNodeOfType(unwrappedCandidateTest, "LogicalExpression") && (unwrappedCandidateTest.operator === "&&" || unwrappedCandidateTest.operator === "||")) {
6057
+ pendingTests.push(unwrappedCandidateTest.left, unwrappedCandidateTest.right);
6058
+ continue;
6059
+ }
6060
+ if (!isProvenFreshnessComparison(unwrappedCandidateTest, scopes, functionScope)) return false;
6061
+ }
6062
+ return true;
5383
6063
  };
5384
6064
  const guardConsequentPerformsSideEffects = (consequent) => {
5385
6065
  if (!consequent) return false;
@@ -5484,7 +6164,9 @@ const asyncDeferAwait = defineRule({
5484
6164
  statementIndex = window.guardCandidateIndex - 1;
5485
6165
  continue;
5486
6166
  }
5487
- if (isCancellationGuardTest(guardStatement.test) || guardTestReadsMutableEnvironment(guardStatement.test) || isNonLiteralComparisonTest(guardStatement.test) || guardTestReadsReassignedLocal(guardStatement.test, guardStatement) || guardConsequentPerformsSideEffects(guardStatement.consequent)) {
6167
+ const enclosingFunction = findEnclosingFunction(guardStatement);
6168
+ const functionScope = enclosingFunction ? context.scopes.ownScopeFor(enclosingFunction) : null;
6169
+ if (isCancellationGuardTest(guardStatement.test) || guardTestReadsMutableEnvironment(guardStatement.test) || isNonLiteralComparisonTest(guardStatement.test) || isLogicalCompositionOfFreshnessComparisons(guardStatement.test, context.scopes, functionScope) || guardTestReadsReassignedLocal(guardStatement.test, guardStatement) || guardConsequentPerformsSideEffects(guardStatement.consequent)) {
5488
6170
  statementIndex = window.guardCandidateIndex - 1;
5489
6171
  continue;
5490
6172
  }
@@ -5523,6 +6205,23 @@ const asyncDeferAwait = defineRule({
5523
6205
  }
5524
6206
  });
5525
6207
  //#endregion
6208
+ //#region src/plugin/utils/expression-reads-pattern-binding.ts
6209
+ const expressionReadsPatternBinding = (expression, patterns, scopes) => {
6210
+ const bindingSymbolIds = /* @__PURE__ */ new Set();
6211
+ for (const pattern of patterns) walkAst(pattern, (child) => {
6212
+ if (!isNodeOfType(child, "Identifier")) return;
6213
+ const symbol = scopes.symbolFor(child);
6214
+ if (symbol?.bindingIdentifier === child) bindingSymbolIds.add(symbol.id);
6215
+ });
6216
+ let didReadBinding = false;
6217
+ walkAst(expression, (child) => {
6218
+ if (didReadBinding || !isNodeOfType(child, "Identifier")) return;
6219
+ const reference = scopes.referenceFor(child);
6220
+ if (reference?.flag !== "write" && reference?.resolvedSymbol && bindingSymbolIds.has(reference.resolvedSymbol.id)) didReadBinding = true;
6221
+ });
6222
+ return didReadBinding;
6223
+ };
6224
+ //#endregion
5526
6225
  //#region src/plugin/utils/get-callee-identifier-trail.ts
5527
6226
  const getCalleeIdentifierTrail = (call) => {
5528
6227
  let entry = call;
@@ -5628,16 +6327,12 @@ const isInsideTransactionCallback = (node) => {
5628
6327
  return false;
5629
6328
  };
5630
6329
  const reportIfIndependent = (statements, context) => {
5631
- const declaredNames = /* @__PURE__ */ new Set();
6330
+ const declaredPatterns = [];
5632
6331
  for (const statement of statements) {
5633
6332
  const awaitArgument = getAwaitedCall(statement);
5634
6333
  if (!awaitArgument) continue;
5635
- let referencesEarlierResult = false;
5636
- walkAst(awaitArgument, (child) => {
5637
- if (isNodeOfType(child, "Identifier") && declaredNames.has(child.name)) referencesEarlierResult = true;
5638
- });
5639
- if (referencesEarlierResult) return;
5640
- if (isNodeOfType(statement, "VariableDeclaration") && statement.declarations[0]?.id) collectPatternNames(statement.declarations[0].id, declaredNames);
6334
+ if (expressionReadsPatternBinding(awaitArgument, declaredPatterns, context.scopes)) return;
6335
+ if (isNodeOfType(statement, "VariableDeclaration") && statement.declarations[0]?.id) declaredPatterns.push(statement.declarations[0].id);
5641
6336
  }
5642
6337
  context.report({
5643
6338
  node: statements[0],
@@ -7121,12 +7816,6 @@ const clientLocalstorageNoVersion = defineRule({
7121
7816
  }
7122
7817
  });
7123
7818
  //#endregion
7124
- //#region src/plugin/utils/get-direct-const-initializer.ts
7125
- const getDirectConstInitializer = (symbol) => {
7126
- if (symbol.kind !== "const" || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
7127
- return symbol.initializer;
7128
- };
7129
- //#endregion
7130
7819
  //#region src/plugin/utils/get-range-start.ts
7131
7820
  const getRangeStart = (node) => {
7132
7821
  const rangeStart = node.range?.[0];
@@ -7787,10 +8476,21 @@ const isDestructuredReactApiBinding = (identifier, apiNames, scopes, options) =>
7787
8476
  };
7788
8477
  const isReactApiCall = (node, apiNames, scopes, options = {}) => {
7789
8478
  if (!isNodeOfType(node, "CallExpression")) return false;
7790
- const callee = stripParenExpression(node.callee);
8479
+ return isReactApiCallee(node.callee, apiNames, scopes, options, /* @__PURE__ */ new Set());
8480
+ };
8481
+ const isReactApiCallee = (rawCallee, apiNames, scopes, options, visitedSymbolIds) => {
8482
+ const callee = stripParenExpression(rawCallee);
8483
+ if (options.resolveConditionalAliases && isNodeOfType(callee, "ConditionalExpression")) return isReactApiCallee(callee.consequent, apiNames, scopes, options, new Set(visitedSymbolIds)) && isReactApiCallee(callee.alternate, apiNames, scopes, options, new Set(visitedSymbolIds));
7791
8484
  if (isNodeOfType(callee, "Identifier")) {
7792
8485
  if (isNamedReactApiImport(callee, apiNames, scopes, Boolean(options.resolveNamedAliases))) return true;
7793
8486
  if (options.resolveNamedAliases && isDestructuredReactApiBinding(callee, apiNames, scopes, options)) return true;
8487
+ if (options.resolveConditionalAliases) {
8488
+ const symbol = scopes.symbolFor(callee);
8489
+ if (symbol?.kind === "const" && symbol.initializer && !visitedSymbolIds.has(symbol.id)) {
8490
+ visitedSymbolIds.add(symbol.id);
8491
+ return isReactApiCallee(symbol.initializer, apiNames, scopes, options, visitedSymbolIds);
8492
+ }
8493
+ }
7794
8494
  return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
7795
8495
  }
7796
8496
  if (!isNodeOfType(callee, "MemberExpression") || !includesApiName(apiNames, getStaticPropertyName(callee) ?? "")) return false;
@@ -10812,14 +11512,6 @@ const displayName = defineRule({
10812
11512
  }
10813
11513
  });
10814
11514
  //#endregion
10815
- //#region src/plugin/utils/read-static-boolean.ts
10816
- const readStaticBoolean = (node) => {
10817
- if (!node) return null;
10818
- const unwrappedNode = stripParenExpression(node);
10819
- if (!isNodeOfType(unwrappedNode, "Literal") || typeof unwrappedNode.value !== "boolean") return null;
10820
- return unwrappedNode.value;
10821
- };
10822
- //#endregion
10823
11515
  //#region src/plugin/rules/state-and-effects/utils/build-listener-cleanup-mismatch-message.ts
10824
11516
  const buildListenerCleanupMismatchMessage = (eventName, registrationCapture, removalCapture, callbackComparison) => {
10825
11517
  const hasCallbackMismatch = callbackComparison === "different";
@@ -11655,6 +12347,10 @@ const isConstAliasOfGlobalArrayFrom = (node, scopes) => {
11655
12347
  };
11656
12348
  const executesDuringRender = (functionNode, scopes) => {
11657
12349
  const parent = functionNode.parent;
12350
+ if (isNodeOfType(parent, "NewExpression")) {
12351
+ const callee = stripParenExpression(parent.callee);
12352
+ return Boolean(scopes && parent.arguments?.[0] === functionNode && isNodeOfType(callee, "Identifier") && callee.name === "Promise" && scopes.isGlobalReference(callee));
12353
+ }
11658
12354
  if (!isNodeOfType(parent, "CallExpression")) return false;
11659
12355
  if (parent.callee === functionNode) return true;
11660
12356
  if ((scopes ? isReactApiCall(parent, REACT_RENDER_PHASE_HOOK_NAMES, scopes, { allowGlobalReactNamespace: true }) : isHookCall$2(parent, REACT_RENDER_PHASE_HOOK_NAMES)) && parent.arguments?.[0] === functionNode) return true;
@@ -11674,7 +12370,7 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
11674
12370
  };
11675
12371
  //#endregion
11676
12372
  //#region src/plugin/utils/get-function-binding-name.ts
11677
- const getFunctionBindingIdentifier = (functionNode) => {
12373
+ const getFunctionBindingIdentifier$1 = (functionNode) => {
11678
12374
  if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
11679
12375
  const parent = functionNode.parent;
11680
12376
  if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) return parent.id;
@@ -11685,7 +12381,7 @@ const getFunctionBindingIdentifier = (functionNode) => {
11685
12381
  }
11686
12382
  return null;
11687
12383
  };
11688
- const getFunctionBindingName$1 = (functionNode) => getFunctionBindingIdentifier(functionNode)?.name ?? null;
12384
+ const getFunctionBindingName$1 = (functionNode) => getFunctionBindingIdentifier$1(functionNode)?.name ?? null;
11689
12385
  //#endregion
11690
12386
  //#region src/plugin/utils/is-event-handler-attribute.ts
11691
12387
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && /^on[A-Z]/.test(node.name.name);
@@ -12272,7 +12968,7 @@ const findDirectCallForReference = (identifier) => {
12272
12968
  return isNodeOfType(callNode, "CallExpression") && callNode.callee === expressionRoot ? callNode : null;
12273
12969
  };
12274
12970
  const findSingleDirectInvocation = (functionNode, caller, context) => {
12275
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
12971
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
12276
12972
  if (!bindingIdentifier || resolveStableValue(bindingIdentifier, context) !== functionNode) return null;
12277
12973
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12278
12974
  if (!symbol) return null;
@@ -12809,7 +13505,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
12809
13505
  };
12810
13506
  const isPotentiallyReachableFunction = (functionNode, context) => {
12811
13507
  if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode)) return true;
12812
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
13508
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
12813
13509
  if (!bindingIdentifier) return false;
12814
13510
  const symbol = context.scopes.symbolFor(bindingIdentifier);
12815
13511
  if (!symbol) return false;
@@ -13043,7 +13739,7 @@ const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) =
13043
13739
  return didFindCleanupEffect;
13044
13740
  };
13045
13741
  const isUseSyncExternalStoreSubscribeFunction = (functionNode, context) => {
13046
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
13742
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
13047
13743
  if (!bindingIdentifier) return false;
13048
13744
  const visitedSymbolIds = /* @__PURE__ */ new Set();
13049
13745
  const isSubscribeBinding = (candidateBinding) => {
@@ -13211,517 +13907,6 @@ const effectNeedsCleanup = defineRule({
13211
13907
  }
13212
13908
  });
13213
13909
  //#endregion
13214
- //#region src/plugin/semantic/scope-analysis.ts
13215
- const isHoistedBindingKind = (kind) => kind === "var" || kind === "function";
13216
- const findHoistTargetScope = (scope) => {
13217
- let current = scope;
13218
- while (current) {
13219
- if (current.kind === "module" || current.kind === "function" || current.kind === "arrow-function" || current.kind === "method") return current;
13220
- current = current.parent;
13221
- }
13222
- return scope;
13223
- };
13224
- const createScope = (kind, node, parent, state) => {
13225
- const scope = {
13226
- id: state.nextScopeId++,
13227
- kind,
13228
- node,
13229
- parent,
13230
- children: [],
13231
- symbols: [],
13232
- references: [],
13233
- symbolsByName: /* @__PURE__ */ new Map()
13234
- };
13235
- if (parent) parent.children.push(scope);
13236
- return scope;
13237
- };
13238
- const pushScope = (kind, node, state) => {
13239
- const scope = createScope(kind, node, state.currentScope, state);
13240
- state.scopeStack.push(scope);
13241
- state.currentScope = scope;
13242
- state.ownScopeForNode.set(node, scope);
13243
- return scope;
13244
- };
13245
- const popScope = (state) => {
13246
- state.scopeStack.pop();
13247
- const previous = state.scopeStack[state.scopeStack.length - 1];
13248
- if (!previous) throw new Error("scope stack underflow");
13249
- state.currentScope = previous;
13250
- };
13251
- const recordSymbol = (scope, state, options) => {
13252
- const symbol = {
13253
- id: state.nextSymbolId++,
13254
- name: options.name,
13255
- kind: options.kind,
13256
- bindingIdentifier: options.bindingIdentifier,
13257
- declarationNode: options.declarationNode,
13258
- scope,
13259
- initializer: options.initializer,
13260
- references: []
13261
- };
13262
- scope.symbols.push(symbol);
13263
- scope.symbolsByName.set(options.name, symbol);
13264
- state.symbolByBindingIdentifier.set(options.bindingIdentifier, symbol);
13265
- return symbol;
13266
- };
13267
- const collectBindingNamesFromPattern = (pattern) => {
13268
- const out = [];
13269
- const visit = (node) => {
13270
- if (isNodeOfType(node, "Identifier")) {
13271
- out.push(node);
13272
- return;
13273
- }
13274
- if (isNodeOfType(node, "ObjectPattern")) {
13275
- for (const property of node.properties) if (isNodeOfType(property, "Property")) {
13276
- const propValue = property.value;
13277
- visit(propValue);
13278
- } else if (isNodeOfType(property, "RestElement")) visit(property.argument);
13279
- return;
13280
- }
13281
- if (isNodeOfType(node, "ArrayPattern")) {
13282
- for (const element of node.elements) if (element) visit(element);
13283
- return;
13284
- }
13285
- if (isNodeOfType(node, "RestElement")) {
13286
- visit(node.argument);
13287
- return;
13288
- }
13289
- if (isNodeOfType(node, "AssignmentPattern")) visit(node.left);
13290
- };
13291
- visit(pattern);
13292
- return out;
13293
- };
13294
- const visitDestructuringDeclarations = (pattern, baseInitializer, scope, state, symbolKind, declarationNode) => {
13295
- if (isNodeOfType(pattern, "Identifier")) {
13296
- recordSymbol(scope, state, {
13297
- name: pattern.name,
13298
- kind: symbolKind,
13299
- bindingIdentifier: pattern,
13300
- declarationNode,
13301
- initializer: baseInitializer
13302
- });
13303
- return;
13304
- }
13305
- if (isNodeOfType(pattern, "ObjectPattern")) {
13306
- for (const property of pattern.properties) if (isNodeOfType(property, "Property")) {
13307
- const propertyValue = property.value;
13308
- visitDestructuringDeclarations(propertyValue, isNodeOfType(propertyValue, "AssignmentPattern") ? propertyValue.right : baseInitializer, scope, state, symbolKind, declarationNode);
13309
- } else if (isNodeOfType(property, "RestElement")) visitDestructuringDeclarations(property.argument, baseInitializer, scope, state, symbolKind, declarationNode);
13310
- return;
13311
- }
13312
- if (isNodeOfType(pattern, "ArrayPattern")) {
13313
- for (const element of pattern.elements) {
13314
- if (!element) continue;
13315
- visitDestructuringDeclarations(element, isNodeOfType(element, "AssignmentPattern") ? element.right ?? null : baseInitializer, scope, state, symbolKind, declarationNode);
13316
- }
13317
- return;
13318
- }
13319
- if (isNodeOfType(pattern, "AssignmentPattern")) {
13320
- visitDestructuringDeclarations(pattern.left, pattern.right ?? null, scope, state, symbolKind, declarationNode);
13321
- return;
13322
- }
13323
- if (isNodeOfType(pattern, "RestElement")) visitDestructuringDeclarations(pattern.argument, null, scope, state, symbolKind, declarationNode);
13324
- };
13325
- const tagAsBinding = (state, identifier) => {
13326
- bindingPositionMarker.add(identifier);
13327
- };
13328
- const bindingPositionMarker = /* @__PURE__ */ new WeakSet();
13329
- const recordReference = (state, identifier, flag) => {
13330
- const reference = {
13331
- id: state.nextReferenceId++,
13332
- identifier,
13333
- resolvedSymbol: null,
13334
- flag,
13335
- scope: state.currentScope
13336
- };
13337
- state.currentScope.references.push(reference);
13338
- state.referenceByIdentifier.set(identifier, reference);
13339
- };
13340
- const isFunctionBodyBlock = (block) => {
13341
- if (!block.parent) return false;
13342
- return isFunctionLike$1(block.parent);
13343
- };
13344
- const isCatchClauseBlock = (block) => block.parent !== null && block.parent !== void 0 && block.parent.type === "CatchClause";
13345
- const handleVariableDeclaration = (declaration, state) => {
13346
- if (!isNodeOfType(declaration, "VariableDeclaration")) return;
13347
- const symbolKind = declaration.kind === "var" ? "var" : declaration.kind === "let" ? "let" : declaration.kind === "const" ? "const" : "using";
13348
- const targetScope = isHoistedBindingKind(symbolKind) ? findHoistTargetScope(state.currentScope) : state.currentScope;
13349
- for (const declarator of declaration.declarations) {
13350
- const declaratorNode = declarator;
13351
- const init = declarator.init ?? null;
13352
- visitDestructuringDeclarations(declarator.id, init, targetScope, state, symbolKind, declaratorNode);
13353
- for (const identifier of collectBindingNamesFromPattern(declarator.id)) tagAsBinding(state, identifier);
13354
- }
13355
- };
13356
- const handleFunctionDeclaration = (fn, state) => {
13357
- if (!isNodeOfType(fn, "FunctionDeclaration")) return;
13358
- if (fn.id) {
13359
- recordSymbol(findHoistTargetScope(state.currentScope), state, {
13360
- name: fn.id.name,
13361
- kind: "function",
13362
- bindingIdentifier: fn.id,
13363
- declarationNode: fn,
13364
- initializer: fn
13365
- });
13366
- tagAsBinding(state, fn.id);
13367
- }
13368
- };
13369
- const handleClassDeclaration = (cls, state) => {
13370
- if (!isNodeOfType(cls, "ClassDeclaration")) return;
13371
- if (cls.id) {
13372
- recordSymbol(state.currentScope, state, {
13373
- name: cls.id.name,
13374
- kind: "class",
13375
- bindingIdentifier: cls.id,
13376
- declarationNode: cls,
13377
- initializer: cls
13378
- });
13379
- tagAsBinding(state, cls.id);
13380
- }
13381
- };
13382
- const handleImportDeclaration = (importDeclaration, state) => {
13383
- if (!isNodeOfType(importDeclaration, "ImportDeclaration")) return;
13384
- const target = findHoistTargetScope(state.currentScope);
13385
- for (const specifier of importDeclaration.specifiers) {
13386
- const local = specifier.local;
13387
- if (!isNodeOfType(local, "Identifier")) continue;
13388
- recordSymbol(target, state, {
13389
- name: local.name,
13390
- kind: "import",
13391
- bindingIdentifier: local,
13392
- declarationNode: specifier,
13393
- initializer: specifier
13394
- });
13395
- tagAsBinding(state, local);
13396
- }
13397
- };
13398
- const handleTsDeclarations = (node, state) => {
13399
- if (node.type !== "TSImportEqualsDeclaration" && node.type !== "TSEnumDeclaration" && node.type !== "TSTypeAliasDeclaration" && node.type !== "TSInterfaceDeclaration" && node.type !== "TSModuleDeclaration") return;
13400
- const idNode = node.id;
13401
- if (!idNode || !isNodeOfType(idNode, "Identifier")) return;
13402
- const kind = node.type === "TSImportEqualsDeclaration" ? "ts-import-equals" : node.type === "TSEnumDeclaration" ? "ts-enum" : node.type === "TSTypeAliasDeclaration" ? "ts-type-alias" : node.type === "TSInterfaceDeclaration" ? "ts-interface" : "ts-module";
13403
- recordSymbol(findHoistTargetScope(state.currentScope), state, {
13404
- name: idNode.name,
13405
- kind,
13406
- bindingIdentifier: idNode,
13407
- declarationNode: node,
13408
- initializer: null
13409
- });
13410
- tagAsBinding(state, idNode);
13411
- };
13412
- const handleFunctionParameters = (params, scope, state) => {
13413
- for (const param of params) {
13414
- visitDestructuringDeclarations(param, null, scope, state, "parameter", param);
13415
- for (const identifier of collectBindingNamesFromPattern(param)) tagAsBinding(state, identifier);
13416
- }
13417
- };
13418
- const shouldPushBlockScope = (block) => {
13419
- if (!isNodeOfType(block, "BlockStatement")) return false;
13420
- if (isFunctionBodyBlock(block)) return false;
13421
- if (isCatchClauseBlock(block)) return false;
13422
- if (block.parent && block.parent.type === "TSModuleDeclaration") return false;
13423
- return true;
13424
- };
13425
- const isJsxIdentifierBindingReference = (identifier) => {
13426
- const parent = identifier.parent;
13427
- if (!parent) return false;
13428
- if (parent.type === "JSXMemberExpression") return parent.object === identifier;
13429
- if (parent.type === "JSXNamespacedName") return false;
13430
- const ASCII_LOWERCASE_A = 97;
13431
- const ASCII_LOWERCASE_Z = 122;
13432
- const firstCharCode = identifier.name.charCodeAt(0);
13433
- if (firstCharCode >= ASCII_LOWERCASE_A && firstCharCode <= ASCII_LOWERCASE_Z) return false;
13434
- return true;
13435
- };
13436
- const isNonReferencePosition = (identifier) => {
13437
- const parent = identifier.parent;
13438
- if (!parent) return false;
13439
- switch (parent.type) {
13440
- case "MemberExpression": return parent.property === identifier && !parent.computed;
13441
- case "Property": return parent.key === identifier && !parent.computed && !parent.shorthand;
13442
- case "MethodDefinition":
13443
- case "PropertyDefinition": return parent.key === identifier && !parent.computed;
13444
- case "JSXAttribute": return parent.name === identifier;
13445
- case "ImportSpecifier": return parent.imported === identifier;
13446
- case "ExportSpecifier": return parent.exported === identifier && parent.local !== parent.exported;
13447
- case "LabeledStatement":
13448
- case "BreakStatement":
13449
- case "ContinueStatement": return parent.label === identifier;
13450
- default: return false;
13451
- }
13452
- };
13453
- const inferReferenceFlag = (identifier) => {
13454
- const parent = identifier.parent;
13455
- if (!parent) return "read";
13456
- switch (parent.type) {
13457
- case "AssignmentExpression":
13458
- if (parent.left === identifier) return parent.operator === "=" ? "write" : "read-write";
13459
- return "read";
13460
- case "UpdateExpression": return "read-write";
13461
- case "ForInStatement":
13462
- case "ForOfStatement": return parent.left === identifier ? "write" : "read";
13463
- default: return "read";
13464
- }
13465
- };
13466
- const setNodeScope = (node, state) => {
13467
- state.nodeScope.set(node, state.currentScope);
13468
- };
13469
- const walkParameterReferences = (pattern, state) => {
13470
- if (isNodeOfType(pattern, "AssignmentPattern")) {
13471
- walkParameterReferences(pattern.left, state);
13472
- const defaultValue = pattern.right ?? null;
13473
- if (defaultValue) walk(defaultValue, state);
13474
- return;
13475
- }
13476
- if (isNodeOfType(pattern, "ObjectPattern")) {
13477
- for (const property of pattern.properties) {
13478
- const propertyNode = property;
13479
- if (isNodeOfType(propertyNode, "RestElement")) {
13480
- walkParameterReferences(propertyNode.argument, state);
13481
- continue;
13482
- }
13483
- if (!isNodeOfType(propertyNode, "Property")) continue;
13484
- const propertyDetail = propertyNode;
13485
- if (propertyDetail.computed) walk(propertyDetail.key, state);
13486
- walkParameterReferences(propertyDetail.value, state);
13487
- }
13488
- return;
13489
- }
13490
- if (isNodeOfType(pattern, "ArrayPattern")) {
13491
- for (const element of pattern.elements) if (element) walkParameterReferences(element, state);
13492
- return;
13493
- }
13494
- if (isNodeOfType(pattern, "RestElement")) walkParameterReferences(pattern.argument, state);
13495
- };
13496
- const walk = (node, state) => {
13497
- if (isFunctionLike$1(node)) {
13498
- if (isNodeOfType(node, "FunctionDeclaration") && node.id) handleFunctionDeclaration(node, state);
13499
- const functionParams = node.params ?? [];
13500
- for (const param of functionParams) {
13501
- if (!("decorators" in param) || !Array.isArray(param.decorators)) continue;
13502
- for (const decorator of param.decorators) if (isAstNode(decorator)) walk(decorator, state);
13503
- }
13504
- setNodeScope(node, state);
13505
- const fnScope = pushScope(node.type === "ArrowFunctionExpression" ? "arrow-function" : "function", node, state);
13506
- if ((isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "FunctionDeclaration")) && node.id) {
13507
- recordSymbol(fnScope, state, {
13508
- name: node.id.name,
13509
- kind: "function",
13510
- bindingIdentifier: node.id,
13511
- declarationNode: node,
13512
- initializer: node
13513
- });
13514
- tagAsBinding(state, node.id);
13515
- }
13516
- handleFunctionParameters(functionParams, fnScope, state);
13517
- for (const param of functionParams) walkParameterReferences(param, state);
13518
- const body = node.body;
13519
- if (body) walk(body, state);
13520
- popScope(state);
13521
- return;
13522
- }
13523
- if (isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression")) {
13524
- if (isNodeOfType(node, "ClassDeclaration") && node.id) handleClassDeclaration(node, state);
13525
- if (Array.isArray(node.decorators)) {
13526
- for (const decorator of node.decorators) if (isAstNode(decorator)) walk(decorator, state);
13527
- }
13528
- const classScope = pushScope("class", node, state);
13529
- setNodeScope(node, state);
13530
- if (isNodeOfType(node, "ClassExpression") && node.id) {
13531
- recordSymbol(classScope, state, {
13532
- name: node.id.name,
13533
- kind: "class",
13534
- bindingIdentifier: node.id,
13535
- declarationNode: node,
13536
- initializer: node
13537
- });
13538
- tagAsBinding(state, node.id);
13539
- }
13540
- if (node.superClass) walk(node.superClass, state);
13541
- if (node.body) walk(node.body, state);
13542
- popScope(state);
13543
- return;
13544
- }
13545
- if (isNodeOfType(node, "CatchClause")) {
13546
- const catchScope = pushScope("catch", node, state);
13547
- setNodeScope(node, state);
13548
- if (node.param) {
13549
- visitDestructuringDeclarations(node.param, null, catchScope, state, "catch-clause-parameter", node);
13550
- for (const identifier of collectBindingNamesFromPattern(node.param)) tagAsBinding(state, identifier);
13551
- }
13552
- if (node.body) walk(node.body, state);
13553
- popScope(state);
13554
- return;
13555
- }
13556
- if (isNodeOfType(node, "ForStatement") || isNodeOfType(node, "ForInStatement") || isNodeOfType(node, "ForOfStatement")) {
13557
- pushScope("for", node, state);
13558
- setNodeScope(node, state);
13559
- const nodeRecord = node;
13560
- for (const key of Object.keys(nodeRecord)) {
13561
- if (key === "parent") continue;
13562
- if (TYPE_POSITION_CHILD_KEYS.has(key)) continue;
13563
- const child = nodeRecord[key];
13564
- if (Array.isArray(child)) {
13565
- for (const item of child) if (isAstNode(item)) walk(item, state);
13566
- } else if (isAstNode(child)) walk(child, state);
13567
- }
13568
- popScope(state);
13569
- return;
13570
- }
13571
- if (isNodeOfType(node, "SwitchStatement")) {
13572
- pushScope("switch", node, state);
13573
- setNodeScope(node, state);
13574
- if (node.discriminant) walk(node.discriminant, state);
13575
- for (const switchCase of node.cases) walk(switchCase, state);
13576
- popScope(state);
13577
- return;
13578
- }
13579
- if (isNodeOfType(node, "TSModuleDeclaration")) {
13580
- const moduleScope = pushScope("ts-module", node, state);
13581
- setNodeScope(node, state);
13582
- if (node.id && isNodeOfType(node.id, "Identifier")) {
13583
- const identifier = node.id;
13584
- recordSymbol(findHoistTargetScope(moduleScope.parent ?? state.currentScope), state, {
13585
- name: identifier.name,
13586
- kind: "ts-module",
13587
- bindingIdentifier: identifier,
13588
- declarationNode: node,
13589
- initializer: null
13590
- });
13591
- tagAsBinding(state, identifier);
13592
- }
13593
- if (node.body) walk(node.body, state);
13594
- popScope(state);
13595
- return;
13596
- }
13597
- if (isNodeOfType(node, "TSEnumDeclaration")) {
13598
- handleTsDeclarations(node, state);
13599
- pushScope("ts-enum", node, state);
13600
- setNodeScope(node, state);
13601
- const members = node.members ?? [];
13602
- for (const member of members) walk(member, state);
13603
- popScope(state);
13604
- return;
13605
- }
13606
- if (isNodeOfType(node, "BlockStatement") && shouldPushBlockScope(node)) {
13607
- pushScope("block", node, state);
13608
- setNodeScope(node, state);
13609
- for (const statement of node.body) walk(statement, state);
13610
- popScope(state);
13611
- return;
13612
- }
13613
- setNodeScope(node, state);
13614
- if (isNodeOfType(node, "VariableDeclaration")) handleVariableDeclaration(node, state);
13615
- else if (isNodeOfType(node, "FunctionDeclaration")) handleFunctionDeclaration(node, state);
13616
- else if (isNodeOfType(node, "ClassDeclaration")) handleClassDeclaration(node, state);
13617
- else if (isNodeOfType(node, "ImportDeclaration")) handleImportDeclaration(node, state);
13618
- else if (node.type === "TSImportEqualsDeclaration" || node.type === "TSTypeAliasDeclaration" || node.type === "TSInterfaceDeclaration") handleTsDeclarations(node, state);
13619
- if ((isNodeOfType(node, "Identifier") || isNodeOfType(node, "JSXIdentifier")) && !bindingPositionMarker.has(node) && !isNonReferencePosition(node)) if (isNodeOfType(node, "JSXIdentifier")) {
13620
- if (isJsxIdentifierBindingReference(node)) recordReference(state, node, inferReferenceFlag(node));
13621
- } else recordReference(state, node, inferReferenceFlag(node));
13622
- const nodeRecord = node;
13623
- for (const key of Object.keys(nodeRecord)) {
13624
- if (key === "parent") continue;
13625
- if (TYPE_POSITION_CHILD_KEYS.has(key)) continue;
13626
- const child = nodeRecord[key];
13627
- if (Array.isArray(child)) {
13628
- for (const item of child) if (isAstNode(item)) walk(item, state);
13629
- } else if (isAstNode(child)) walk(child, state);
13630
- }
13631
- };
13632
- const resolveReferences = (rootScope) => {
13633
- const visitScope = (scope) => {
13634
- for (const reference of scope.references) {
13635
- const name = reference.identifier.name;
13636
- if (typeof name !== "string") continue;
13637
- let lookup = scope;
13638
- while (lookup) {
13639
- const found = lookup.symbolsByName.get(name);
13640
- if (found) {
13641
- reference.resolvedSymbol = found;
13642
- found.references.push(reference);
13643
- break;
13644
- }
13645
- lookup = lookup.parent;
13646
- }
13647
- }
13648
- for (const child of scope.children) visitScope(child);
13649
- };
13650
- visitScope(rootScope);
13651
- };
13652
- const analyzeScopes = (program) => {
13653
- const rootScope = {
13654
- id: 0,
13655
- kind: "module",
13656
- node: program,
13657
- parent: null,
13658
- children: [],
13659
- symbols: [],
13660
- references: [],
13661
- symbolsByName: /* @__PURE__ */ new Map()
13662
- };
13663
- const state = {
13664
- nextScopeId: 1,
13665
- nextSymbolId: 0,
13666
- nextReferenceId: 0,
13667
- currentScope: rootScope,
13668
- scopeStack: [rootScope],
13669
- rootScope,
13670
- nodeScope: /* @__PURE__ */ new WeakMap(),
13671
- ownScopeForNode: /* @__PURE__ */ new WeakMap(),
13672
- symbolByBindingIdentifier: /* @__PURE__ */ new WeakMap(),
13673
- referenceByIdentifier: /* @__PURE__ */ new WeakMap()
13674
- };
13675
- state.nodeScope.set(program, rootScope);
13676
- state.ownScopeForNode.set(program, rootScope);
13677
- if (isNodeOfType(program, "Program")) for (const statement of program.body) walk(statement, state);
13678
- else walk(program, state);
13679
- resolveReferences(rootScope);
13680
- const scopeFor = (node) => {
13681
- let current = node;
13682
- while (current) {
13683
- const scope = state.nodeScope.get(current);
13684
- if (scope) return scope;
13685
- current = current.parent ?? null;
13686
- }
13687
- return rootScope;
13688
- };
13689
- const symbolFor = (identifier) => {
13690
- const reference = state.referenceByIdentifier.get(identifier);
13691
- if (reference) return reference.resolvedSymbol;
13692
- const symbolForBinding = state.symbolByBindingIdentifier.get(identifier);
13693
- if (symbolForBinding) return symbolForBinding;
13694
- return null;
13695
- };
13696
- const referenceFor = (identifier) => {
13697
- return state.referenceByIdentifier.get(identifier) ?? null;
13698
- };
13699
- const isGlobalReference = (identifier) => {
13700
- const reference = state.referenceByIdentifier.get(identifier);
13701
- if (!reference) return false;
13702
- return reference.resolvedSymbol === null;
13703
- };
13704
- const ownScopeFor = (node) => {
13705
- return state.ownScopeForNode.get(node) ?? null;
13706
- };
13707
- return {
13708
- rootScope,
13709
- scopeFor,
13710
- ownScopeFor,
13711
- symbolFor,
13712
- referenceFor,
13713
- isGlobalReference
13714
- };
13715
- };
13716
- const isDescendantScope = (inner, outer) => {
13717
- let current = inner;
13718
- while (current) {
13719
- if (current === outer) return true;
13720
- current = current.parent;
13721
- }
13722
- return false;
13723
- };
13724
- //#endregion
13725
13910
  //#region src/plugin/semantic/closure-captures.ts
13726
13911
  const computeClosureCaptures = (functionNode, scopes) => {
13727
13912
  const functionScope = scopes.ownScopeFor(functionNode) ?? scopes.scopeFor(functionNode);
@@ -17006,8 +17191,86 @@ const htmlNoNestedInteractive = defineRule({
17006
17191
  } })
17007
17192
  });
17008
17193
  //#endregion
17194
+ //#region src/plugin/utils/get-authoritative-jsx-attribute.ts
17195
+ const getAuthoritativeJsxAttribute = (attributes, targetName, isCaseSensitive = true) => {
17196
+ const normalizedTargetName = isCaseSensitive ? targetName : targetName.toLowerCase();
17197
+ for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
17198
+ const attribute = attributes[attributeIndex];
17199
+ if (!attribute || isNodeOfType(attribute, "JSXSpreadAttribute")) return null;
17200
+ if (!isNodeOfType(attribute, "JSXAttribute")) continue;
17201
+ const attributeName = getJsxAttributeName(attribute.name);
17202
+ if ((isCaseSensitive ? attributeName : attributeName?.toLowerCase()) === normalizedTargetName) return attribute;
17203
+ }
17204
+ return null;
17205
+ };
17206
+ //#endregion
17207
+ //#region src/plugin/utils/is-jsx-fragment-element.ts
17208
+ const isJsxFragmentElement = (node, scopes) => {
17209
+ if (!isNodeOfType(node, "JSXOpeningElement")) return false;
17210
+ const elementName = node.name;
17211
+ if (isNodeOfType(elementName, "JSXIdentifier")) {
17212
+ if (!scopes) return elementName.name === "Fragment";
17213
+ const symbol = resolveConstIdentifierAlias(elementName, scopes);
17214
+ if (!symbol) return elementName.name === "Fragment" && scopes.isGlobalReference(elementName);
17215
+ return isImportedFromReact(symbol) && getImportedName(symbol.declarationNode) === "Fragment";
17216
+ }
17217
+ if (isNodeOfType(elementName, "JSXMemberExpression")) {
17218
+ if (!isNodeOfType(elementName.object, "JSXIdentifier")) return false;
17219
+ if (elementName.property.name !== "Fragment") return false;
17220
+ if (!scopes) return elementName.object.name === "React";
17221
+ if (isReactNamespaceImport(elementName.object, scopes)) return true;
17222
+ return elementName.object.name === "React" && scopes.isGlobalReference(elementName.object);
17223
+ }
17224
+ return false;
17225
+ };
17226
+ //#endregion
17009
17227
  //#region src/plugin/rules/a11y/iframe-has-title.ts
17010
17228
  const MESSAGE$51 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
17229
+ const isStaticallyAriaHidden = (openingElement) => {
17230
+ const ariaHiddenAttribute = getAuthoritativeJsxAttribute(openingElement.attributes, "aria-hidden", false);
17231
+ if (!ariaHiddenAttribute) return false;
17232
+ if (!ariaHiddenAttribute.value) return true;
17233
+ const value = ariaHiddenAttribute.value;
17234
+ if (isNodeOfType(value, "Literal")) return value.value === true || value.value === "true";
17235
+ if (!isNodeOfType(value, "JSXExpressionContainer")) return false;
17236
+ return isNodeOfType(value.expression, "Literal") && (value.expression.value === true || value.expression.value === "true");
17237
+ };
17238
+ const hasStaticallyNegativeTabIndex = (openingElement) => {
17239
+ const tabIndexAttribute = getAuthoritativeJsxAttribute(openingElement.attributes, "tabIndex", false);
17240
+ if (!tabIndexAttribute) return false;
17241
+ const value = tabIndexAttribute.value;
17242
+ if (value && isNodeOfType(value, "JSXExpressionContainer") && isNodeOfType(value.expression, "ConditionalExpression") && !isNodeOfType(value.expression.test, "Literal")) return false;
17243
+ const tabIndexValue = parseJsxValue(value);
17244
+ return tabIndexValue !== null && tabIndexValue < 0;
17245
+ };
17246
+ const hasStaticallyDecorativeRole = (openingElement, scopes) => {
17247
+ const roleAttribute = getAuthoritativeJsxAttribute(openingElement.attributes, "role", false);
17248
+ if (!roleAttribute) return false;
17249
+ const roleCandidates = getJsxPropStaticStringValues(roleAttribute, scopes);
17250
+ return roleCandidates !== null && roleCandidates.length > 0 && roleCandidates.every((roleCandidate) => {
17251
+ const firstValidRole = roleCandidate.split(/\s+/).find((roleToken) => VALID_ARIA_ROLES.has(roleToken));
17252
+ return firstValidRole !== void 0 && PRESENTATION_ROLES$1.has(firstValidRole);
17253
+ });
17254
+ };
17255
+ const isInsideStaticallyHiddenJsxSubtree = (openingElement, scopes) => {
17256
+ if (isStaticallyAriaHidden(openingElement)) return true;
17257
+ let ancestor = openingElement.parent?.parent;
17258
+ while (ancestor) {
17259
+ if (isNodeOfType(ancestor, "JSXAttribute")) {
17260
+ if (!(isNodeOfType(ancestor.name, "JSXIdentifier") && ancestor.name.name === "children")) return false;
17261
+ }
17262
+ if (isNodeOfType(ancestor, "JSXElement") && !isJsxFragmentElement(ancestor.openingElement, scopes)) {
17263
+ const ancestorName = ancestor.openingElement.name;
17264
+ if (!isNodeOfType(ancestorName, "JSXIdentifier")) return false;
17265
+ const firstCharacter = ancestorName.name[0];
17266
+ if (!(firstCharacter === firstCharacter?.toLowerCase())) return false;
17267
+ if (isStaticallyAriaHidden(ancestor.openingElement)) return true;
17268
+ }
17269
+ if (isNodeOfType(ancestor, "CallExpression") || isNodeOfType(ancestor, "NewExpression") || isNodeOfType(ancestor, "VariableDeclarator") || isNodeOfType(ancestor, "AssignmentExpression") || isNodeOfType(ancestor, "Property") || isFunctionLike$1(ancestor) || isNodeOfType(ancestor, "Program")) return false;
17270
+ ancestor = ancestor.parent;
17271
+ }
17272
+ return false;
17273
+ };
17011
17274
  const evaluateTitleValue = (value) => {
17012
17275
  if (!value) return "missing";
17013
17276
  if (isNodeOfType(value, "Literal")) {
@@ -17042,6 +17305,9 @@ const iframeHasTitle = defineRule({
17042
17305
  create: (context) => ({ JSXOpeningElement(node) {
17043
17306
  const tag = getElementType(node, context.settings);
17044
17307
  if (tag !== "iframe") return;
17308
+ if (isInsideStaticallyHiddenJsxSubtree(node, context.scopes)) return;
17309
+ if (hasStaticallyNegativeTabIndex(node)) return;
17310
+ if (hasStaticallyDecorativeRole(node, context.scopes)) return;
17045
17311
  const hasSpread = node.attributes.some((attribute) => isNodeOfType(attribute, "JSXSpreadAttribute"));
17046
17312
  const titleAttr = hasJsxPropIgnoreCase(node.attributes, "title");
17047
17313
  if (!titleAttr) {
@@ -17574,7 +17840,7 @@ const interactiveSupportsFocus = defineRule({
17574
17840
  if (!hasInteractiveHandler) return;
17575
17841
  const elementType = getElementType(node, context.settings);
17576
17842
  if (!HTML_TAGS.has(elementType)) return;
17577
- if (isDisabledElement(node) || isHiddenFromScreenReader(node, context.settings) || isPresentationRole(node)) return;
17843
+ if (canContentEditableBeTabbable(node, context.scopes, context.settings) || isDisabledElement(node) || isHiddenFromScreenReader(node, context.settings) || isPresentationRole(node)) return;
17578
17844
  const hasTabIndex = Boolean(hasJsxPropIgnoreCase(node.attributes, "tabIndex"));
17579
17845
  const hasId = Boolean(hasJsxPropIgnoreCase(node.attributes, "id"));
17580
17846
  for (const role of roleCandidates) {
@@ -21134,26 +21400,6 @@ const jsxFilenameExtension = defineRule({
21134
21400
  }
21135
21401
  });
21136
21402
  //#endregion
21137
- //#region src/plugin/utils/is-jsx-fragment-element.ts
21138
- const isJsxFragmentElement = (node, scopes) => {
21139
- if (!isNodeOfType(node, "JSXOpeningElement")) return false;
21140
- const elementName = node.name;
21141
- if (isNodeOfType(elementName, "JSXIdentifier")) {
21142
- if (!scopes) return elementName.name === "Fragment";
21143
- const symbol = resolveConstIdentifierAlias(elementName, scopes);
21144
- if (!symbol) return elementName.name === "Fragment" && scopes.isGlobalReference(elementName);
21145
- return isImportedFromReact(symbol) && getImportedName(symbol.declarationNode) === "Fragment";
21146
- }
21147
- if (isNodeOfType(elementName, "JSXMemberExpression")) {
21148
- if (!isNodeOfType(elementName.object, "JSXIdentifier")) return false;
21149
- if (elementName.property.name !== "Fragment") return false;
21150
- if (!scopes) return elementName.object.name === "React";
21151
- if (isReactNamespaceImport(elementName.object, scopes)) return true;
21152
- return elementName.object.name === "React" && scopes.isGlobalReference(elementName.object);
21153
- }
21154
- return false;
21155
- };
21156
- //#endregion
21157
21403
  //#region src/plugin/rules/react-builtins/jsx-fragments.ts
21158
21404
  const SYNTAX_MESSAGE = "`<React.Fragment>` is used where shorthand fragments are configured, so similar wrappers look different across the codebase.";
21159
21405
  const ELEMENT_MESSAGE = "Fragment shorthand is used where explicit fragments are configured, so similar wrappers look different across the codebase.";
@@ -21492,7 +21738,7 @@ const namedCallbackIteratorCallCache = /* @__PURE__ */ new WeakMap();
21492
21738
  const findNamedCallbackIteratorCall = (functionNode) => {
21493
21739
  const cached = namedCallbackIteratorCallCache.get(functionNode);
21494
21740
  if (cached !== void 0) return cached;
21495
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
21741
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
21496
21742
  if (!bindingIdentifier) {
21497
21743
  namedCallbackIteratorCallCache.set(functionNode, null);
21498
21744
  return null;
@@ -22317,7 +22563,7 @@ const isReactSlotType = (typeNode, environment, activeDeclarations) => {
22317
22563
  }
22318
22564
  return environment.reactSlotTypeBindings.has(typeName);
22319
22565
  };
22320
- const getPropertyName = (property) => {
22566
+ const getPropertyName$1 = (property) => {
22321
22567
  if (!isNodeOfType(property, "TSPropertySignature") || property.computed) return null;
22322
22568
  if (isNodeOfType(property.key, "Identifier")) return property.key.name;
22323
22569
  if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
@@ -22329,7 +22575,7 @@ const collectSlotPropertyNames = (propsType, environment, slotPropertyNames, act
22329
22575
  if (isNodeOfType(propsType, "TSInterfaceDeclaration")) members = propsType.body.body;
22330
22576
  if (members) {
22331
22577
  for (const member of members) {
22332
- const propertyName = getPropertyName(member);
22578
+ const propertyName = getPropertyName$1(member);
22333
22579
  if (!propertyName || !isNodeOfType(member, "TSPropertySignature")) continue;
22334
22580
  const annotation = member.typeAnnotation;
22335
22581
  if (annotation && isNodeOfType(annotation, "TSTypeAnnotation") && isReactSlotType(annotation.typeAnnotation, environment, /* @__PURE__ */ new Set())) slotPropertyNames.add(propertyName);
@@ -26051,6 +26297,7 @@ const nextjsNoClientSideRedirect = defineRule({
26051
26297
  });
26052
26298
  //#endregion
26053
26299
  //#region src/plugin/rules/nextjs/nextjs-no-css-link.ts
26300
+ const HTTP_STYLESHEET_URL_PATTERN = /^https?:\/\//i;
26054
26301
  const nextjsNoCssLink = defineRule({
26055
26302
  id: "nextjs-no-css-link",
26056
26303
  title: "Linked stylesheet bypasses Next.js CSS optimization",
@@ -26064,10 +26311,10 @@ const nextjsNoCssLink = defineRule({
26064
26311
  const relAttribute = findJsxAttribute(attributes, "rel");
26065
26312
  if (!relAttribute?.value) return;
26066
26313
  if ((isNodeOfType(relAttribute.value, "Literal") ? relAttribute.value.value : null) !== "stylesheet") return;
26067
- const hrefAttribute = findJsxAttribute(attributes, "href");
26068
- if (!hrefAttribute?.value) return;
26069
- const hrefValue = isNodeOfType(hrefAttribute.value, "Literal") ? hrefAttribute.value.value : null;
26070
- if (typeof hrefValue === "string" && GOOGLE_FONTS_PATTERN.test(hrefValue)) return;
26314
+ if (!findJsxAttribute(attributes, "href")?.value) return;
26315
+ const authoritativeHrefAttribute = getAuthoritativeJsxAttribute(attributes, "href");
26316
+ const hrefCandidates = authoritativeHrefAttribute ? getJsxPropStaticStringValues(authoritativeHrefAttribute, context.scopes) : null;
26317
+ if (hrefCandidates !== null && hrefCandidates.length > 0 && hrefCandidates.every((hrefCandidate) => HTTP_STYLESHEET_URL_PATTERN.test(hrefCandidate))) return;
26071
26318
  context.report({
26072
26319
  node,
26073
26320
  message: "This <link rel=\"stylesheet\"> bypasses Next.js CSS handling, so the CSS loads unbundled and unoptimized."
@@ -29593,18 +29840,143 @@ const ARITHMETIC_KEY_OPERATORS = new Set([
29593
29840
  "%"
29594
29841
  ]);
29595
29842
  const bindingMutationResultsByProgram = /* @__PURE__ */ new WeakMap();
29596
- const extractCandidateIdentifiers = (expression) => {
29843
+ const UNKNOWN_STATIC_KEY_BRANCH_VALUE = {
29844
+ isNullish: null,
29845
+ isTruthy: null
29846
+ };
29847
+ const mergeStaticKeyBranchValues = (firstValue, secondValue) => ({
29848
+ isNullish: firstValue.isNullish === secondValue.isNullish ? firstValue.isNullish : null,
29849
+ isTruthy: firstValue.isTruthy === secondValue.isTruthy ? firstValue.isTruthy : null
29850
+ });
29851
+ const readStaticKeyBranchValue = (expression, depth) => {
29852
+ if (depth > TYPE_RESOLUTION_DEPTH_LIMIT) return null;
29853
+ const node = stripParenExpression(expression);
29854
+ if (isNodeOfType(node, "Literal")) return {
29855
+ isNullish: node.value === null,
29856
+ isTruthy: Boolean(node.value)
29857
+ };
29858
+ if (isNodeOfType(node, "ArrayExpression") || isNodeOfType(node, "ObjectExpression") || isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ClassExpression") || isNodeOfType(node, "NewExpression")) return {
29859
+ isNullish: false,
29860
+ isTruthy: true
29861
+ };
29862
+ if (isNodeOfType(node, "TemplateLiteral")) return {
29863
+ isNullish: false,
29864
+ isTruthy: (node.quasis ?? []).some((quasi) => typeof quasi.value?.cooked === "string" && quasi.value.cooked.length > 0) ? true : null
29865
+ };
29866
+ if (isNodeOfType(node, "BinaryExpression")) return {
29867
+ isNullish: false,
29868
+ isTruthy: null
29869
+ };
29870
+ if (isNodeOfType(node, "Identifier")) {
29871
+ const binding = findVariableInitializer(node, node.name);
29872
+ if (!binding) {
29873
+ if (node.name === "undefined") return {
29874
+ isNullish: true,
29875
+ isTruthy: false
29876
+ };
29877
+ if (node.name === "NaN") return {
29878
+ isNullish: false,
29879
+ isTruthy: false
29880
+ };
29881
+ if (node.name === "Infinity") return {
29882
+ isNullish: false,
29883
+ isTruthy: true
29884
+ };
29885
+ return null;
29886
+ }
29887
+ if (!isConstDeclaredBinding(binding) || !binding.initializer) return null;
29888
+ const declarator = binding.bindingIdentifier.parent;
29889
+ if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || declarator.id !== binding.bindingIdentifier || declarator.init !== binding.initializer) return null;
29890
+ return readStaticKeyBranchValue(binding.initializer, depth + 1);
29891
+ }
29892
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier")) {
29893
+ if (!Boolean(findVariableInitializer(node.callee, node.callee.name)) && STRING_COERCION_FUNCTIONS.has(node.callee.name)) return {
29894
+ isNullish: false,
29895
+ isTruthy: null
29896
+ };
29897
+ }
29898
+ if (isNodeOfType(node, "UnaryExpression")) {
29899
+ if (node.operator === "void") return {
29900
+ isNullish: true,
29901
+ isTruthy: false
29902
+ };
29903
+ if (node.operator === "typeof") return {
29904
+ isNullish: false,
29905
+ isTruthy: true
29906
+ };
29907
+ if (node.operator === "+" || node.operator === "-" || node.operator === "~") return {
29908
+ isNullish: false,
29909
+ isTruthy: null
29910
+ };
29911
+ if (node.operator !== "!") return null;
29912
+ const argumentValue = readStaticKeyBranchValue(node.argument, depth + 1);
29913
+ if (!argumentValue || argumentValue.isTruthy === null) return null;
29914
+ return {
29915
+ isNullish: false,
29916
+ isTruthy: !argumentValue.isTruthy
29917
+ };
29918
+ }
29919
+ if (isNodeOfType(node, "SequenceExpression")) {
29920
+ const finalExpression = node.expressions.at(-1);
29921
+ return finalExpression ? readStaticKeyBranchValue(finalExpression, depth + 1) : null;
29922
+ }
29923
+ if (isNodeOfType(node, "LogicalExpression")) {
29924
+ const leftValue = readStaticKeyBranchValue(node.left, depth + 1) ?? UNKNOWN_STATIC_KEY_BRANCH_VALUE;
29925
+ if (node.operator === "&&" && leftValue.isTruthy !== null) return leftValue.isTruthy ? readStaticKeyBranchValue(node.right, depth + 1) : leftValue;
29926
+ if (node.operator === "||" && leftValue.isTruthy !== null) return leftValue.isTruthy ? leftValue : readStaticKeyBranchValue(node.right, depth + 1);
29927
+ if (node.operator === "??" && leftValue.isNullish !== null) return leftValue.isNullish ? readStaticKeyBranchValue(node.right, depth + 1) : leftValue;
29928
+ const rightValue = readStaticKeyBranchValue(node.right, depth + 1) ?? UNKNOWN_STATIC_KEY_BRANCH_VALUE;
29929
+ if (node.operator === "||") return {
29930
+ isNullish: rightValue.isNullish === false ? false : null,
29931
+ isTruthy: rightValue.isTruthy === true ? true : null
29932
+ };
29933
+ if (node.operator === "&&") return {
29934
+ isNullish: leftValue.isNullish === false && rightValue.isNullish === false ? false : null,
29935
+ isTruthy: rightValue.isTruthy === false ? false : null
29936
+ };
29937
+ return {
29938
+ isNullish: rightValue.isNullish === false ? false : null,
29939
+ isTruthy: leftValue.isTruthy !== null && leftValue.isTruthy === rightValue.isTruthy ? leftValue.isTruthy : null
29940
+ };
29941
+ }
29942
+ if (isNodeOfType(node, "ConditionalExpression")) {
29943
+ const testValue = readStaticKeyBranchValue(node.test, depth + 1);
29944
+ if (testValue?.isTruthy !== null && testValue?.isTruthy !== void 0) return readStaticKeyBranchValue(testValue.isTruthy ? node.consequent : node.alternate, depth + 1);
29945
+ return mergeStaticKeyBranchValues(readStaticKeyBranchValue(node.consequent, depth + 1) ?? UNKNOWN_STATIC_KEY_BRANCH_VALUE, readStaticKeyBranchValue(node.alternate, depth + 1) ?? UNKNOWN_STATIC_KEY_BRANCH_VALUE);
29946
+ }
29947
+ return null;
29948
+ };
29949
+ const extractCandidateIdentifiers = (expression, depth = 0) => {
29950
+ if (depth > TYPE_RESOLUTION_DEPTH_LIMIT) return [];
29597
29951
  const node = stripParenExpression(expression);
29598
29952
  if (isNodeOfType(node, "Identifier")) return [node];
29599
- if (isNodeOfType(node, "UnaryExpression") && (node.operator === "-" || node.operator === "+" || node.operator === "~") && isNodeOfType(node.argument, "Identifier")) return [node.argument];
29953
+ if (isNodeOfType(node, "UnaryExpression") && (node.operator === "-" || node.operator === "+" || node.operator === "~") && node.argument) return extractCandidateIdentifiers(node.argument, depth + 1);
29600
29954
  if (isNodeOfType(node, "TemplateLiteral")) {
29601
29955
  const identifiers = [];
29602
- for (const templateExpression of node.expressions ?? []) if (isNodeOfType(templateExpression, "Identifier")) identifiers.push(templateExpression);
29956
+ for (const templateExpression of node.expressions ?? []) identifiers.push(...extractCandidateIdentifiers(templateExpression, depth + 1));
29603
29957
  return identifiers;
29604
29958
  }
29959
+ if (isNodeOfType(node, "LogicalExpression")) {
29960
+ const leftValue = readStaticKeyBranchValue(node.left, 0);
29961
+ if (leftValue) {
29962
+ if (node.operator === "&&" && leftValue.isTruthy !== null) return extractCandidateIdentifiers(leftValue.isTruthy ? node.right : node.left, depth + 1);
29963
+ if (node.operator === "||" && leftValue.isTruthy !== null) return extractCandidateIdentifiers(leftValue.isTruthy ? node.left : node.right, depth + 1);
29964
+ if (node.operator === "??" && leftValue.isNullish !== null) return extractCandidateIdentifiers(leftValue.isNullish ? node.right : node.left, depth + 1);
29965
+ }
29966
+ return [...extractCandidateIdentifiers(node.left, depth + 1), ...extractCandidateIdentifiers(node.right, depth + 1)];
29967
+ }
29968
+ if (isNodeOfType(node, "ConditionalExpression")) {
29969
+ const testValue = readStaticKeyBranchValue(node.test, 0);
29970
+ if (testValue && testValue.isTruthy !== null) return extractCandidateIdentifiers(testValue.isTruthy ? node.consequent : node.alternate, depth + 1);
29971
+ return [...extractCandidateIdentifiers(node.consequent, depth + 1), ...extractCandidateIdentifiers(node.alternate, depth + 1)];
29972
+ }
29973
+ if (isNodeOfType(node, "SequenceExpression")) {
29974
+ const finalExpression = node.expressions.at(-1);
29975
+ return finalExpression ? extractCandidateIdentifiers(finalExpression, depth + 1) : [];
29976
+ }
29605
29977
  if (isNodeOfType(node, "CallExpression")) {
29606
- if (isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.object, "Identifier") && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === "toString") return [node.callee.object];
29607
- if (isNodeOfType(node.callee, "Identifier") && STRING_COERCION_FUNCTIONS.has(node.callee.name) && isNodeOfType(node.arguments?.[0], "Identifier")) return [node.arguments[0]];
29978
+ if (isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === "toString") return extractCandidateIdentifiers(node.callee.object, depth + 1);
29979
+ if (isNodeOfType(node.callee, "Identifier") && STRING_COERCION_FUNCTIONS.has(node.callee.name) && !findVariableInitializer(node.callee, node.callee.name) && node.arguments?.[0]) return extractCandidateIdentifiers(node.arguments[0], depth + 1);
29608
29980
  return [];
29609
29981
  }
29610
29982
  if (isNodeOfType(node, "BinaryExpression")) {
@@ -31757,6 +32129,7 @@ const createStateTriggerReachability = ({ analysis, context, effectFunction }) =
31757
32129
  };
31758
32130
  //#endregion
31759
32131
  //#region src/plugin/rules/state-and-effects/no-chain-state-updates.ts
32132
+ const APPLICABLE_EFFECT_HOOK_NAMES = new Set(["useEffect", "useLayoutEffect"]);
31760
32133
  const getUseStateDeclarator = (ref) => (ref.resolved?.defs ?? []).map((def) => def.node).find((node) => isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "ArrayPattern")) ?? null;
31761
32134
  const isDeclaredWithin = (node, container) => {
31762
32135
  let walker = node;
@@ -31808,7 +32181,12 @@ const noChainStateUpdates = defineRule({
31808
32181
  tags: ["test-noise"],
31809
32182
  recommendation: "Set all the related state together in the event handler that starts it, instead of having one useEffect react to a state change and set more state. See https://react.dev/learn/you-might-not-need-an-effect#chains-of-computations",
31810
32183
  create: (context) => ({ CallExpression(node) {
31811
- if (!isUseEffect(node)) return;
32184
+ if (!isReactApiCall(node, APPLICABLE_EFFECT_HOOK_NAMES, context.scopes, {
32185
+ allowGlobalReactNamespace: true,
32186
+ allowUnboundBareCalls: true,
32187
+ resolveConditionalAliases: true,
32188
+ resolveNamedAliases: true
32189
+ })) return;
31812
32190
  const analysis = getProgramAnalysis(node);
31813
32191
  if (!analysis) return;
31814
32192
  if (hasCleanup(analysis, node)) return;
@@ -31988,17 +32366,611 @@ const noCreateContextInRender = defineRule({
31988
32366
  } })
31989
32367
  });
31990
32368
  //#endregion
32369
+ //#region src/plugin/utils/collect-function-children-references.ts
32370
+ const collectFunctionChildrenReferences = (functionNode, scopes) => {
32371
+ if (!isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "FunctionDeclaration")) return null;
32372
+ const rawPropsParameter = functionNode.params[0];
32373
+ const propsParameter = isNodeOfType(rawPropsParameter, "AssignmentPattern") ? rawPropsParameter.left : rawPropsParameter;
32374
+ if (isNodeOfType(propsParameter, "ObjectPattern")) {
32375
+ const childrenProperty = propsParameter.properties.find((property) => isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "children");
32376
+ if (!childrenProperty || !isNodeOfType(childrenProperty, "Property")) return null;
32377
+ const childrenBinding = isNodeOfType(childrenProperty.value, "AssignmentPattern") ? childrenProperty.value.left : childrenProperty.value;
32378
+ if (!isNodeOfType(childrenBinding, "Identifier")) return null;
32379
+ const childrenSymbol = scopes.symbolFor(childrenBinding);
32380
+ if (!childrenSymbol || childrenSymbol.references.length === 0 || childrenSymbol.references.some((reference) => reference.flag !== "read")) return null;
32381
+ return childrenSymbol.references.map((reference) => reference.identifier);
32382
+ }
32383
+ if (!isNodeOfType(propsParameter, "Identifier")) return null;
32384
+ const propsSymbol = scopes.symbolFor(propsParameter);
32385
+ if (!propsSymbol || propsSymbol.references.length === 0) return null;
32386
+ const childrenReferences = [];
32387
+ for (const reference of propsSymbol.references) {
32388
+ if (reference.flag !== "read") return null;
32389
+ const propertyPath = [];
32390
+ let expression = findTransparentExpressionRoot(reference.identifier);
32391
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32392
+ const propertyName = getStaticPropertyName(expression.parent);
32393
+ if (!propertyName) return null;
32394
+ propertyPath.push(propertyName);
32395
+ expression = findTransparentExpressionRoot(expression.parent);
32396
+ }
32397
+ if (propertyPath[0] !== "children") {
32398
+ if (propertyPath.length === 0) return null;
32399
+ continue;
32400
+ }
32401
+ if (propertyPath.length !== 1) return null;
32402
+ childrenReferences.push(expression);
32403
+ }
32404
+ return childrenReferences.length > 0 ? childrenReferences : null;
32405
+ };
32406
+ //#endregion
32407
+ //#region src/plugin/utils/is-proven-react-class-component.ts
32408
+ const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
32409
+ const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
32410
+ const expression = stripParenExpression(node);
32411
+ if (isNodeOfType(expression, "MemberExpression")) {
32412
+ const propertyName = getStaticPropertyName(expression);
32413
+ const receiver = stripParenExpression(expression.object);
32414
+ return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
32415
+ }
32416
+ if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
32417
+ if (!isNodeOfType(expression, "Identifier")) return false;
32418
+ const symbol = scopes.symbolFor(expression);
32419
+ if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
32420
+ visitedSymbolIds.add(symbol.id);
32421
+ if (isImportedFromReact(symbol)) {
32422
+ const importedName = getImportedName(symbol.declarationNode);
32423
+ return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
32424
+ }
32425
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
32426
+ return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
32427
+ };
32428
+ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32429
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
32430
+ visitedClassNodes.add(classNode);
32431
+ return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
32432
+ };
32433
+ //#endregion
32434
+ //#region src/plugin/utils/is-proven-intrinsic-jsx-element.ts
32435
+ const isProvenIntrinsicJsxElement = (openingElement, scopes) => {
32436
+ if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
32437
+ const resolvedElementType = resolveJsxElementType(openingElement);
32438
+ if (resolvedElementType[0] === resolvedElementType[0]?.toLowerCase()) return true;
32439
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
32440
+ const isIntrinsicValue = (node) => {
32441
+ const current = findTransparentExpressionRoot(node);
32442
+ if (isNodeOfType(current, "Literal")) return typeof current.value === "string";
32443
+ if (isNodeOfType(current, "Identifier") || isNodeOfType(current, "JSXIdentifier")) {
32444
+ const symbol = scopes.symbolFor(current);
32445
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return false;
32446
+ visitedSymbolIds.add(symbol.id);
32447
+ const isIntrinsic = isIntrinsicValue(symbol.initializer);
32448
+ visitedSymbolIds.delete(symbol.id);
32449
+ return isIntrinsic;
32450
+ }
32451
+ if (isNodeOfType(current, "ConditionalExpression")) return isIntrinsicValue(current.consequent) && isIntrinsicValue(current.alternate);
32452
+ return false;
32453
+ };
32454
+ return isIntrinsicValue(openingElement.name);
32455
+ };
32456
+ //#endregion
32457
+ //#region src/plugin/rules/react-builtins/is-safe-create-ref-callback-current-write.ts
32458
+ const pathStartsWith$1 = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32459
+ const collectMemberExpression = (identifier) => {
32460
+ let expression = findTransparentExpressionRoot(identifier);
32461
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32462
+ if (!getStaticPropertyName(expression.parent)) return null;
32463
+ expression = findTransparentExpressionRoot(expression.parent);
32464
+ }
32465
+ return expression;
32466
+ };
32467
+ const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
32468
+ const functionExpression = findTransparentExpressionRoot(functionNode);
32469
+ if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
32470
+ const container = functionExpression.parent;
32471
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer")) return false;
32472
+ const attribute = container.parent;
32473
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
32474
+ const openingElement = attribute.parent;
32475
+ return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
32476
+ };
32477
+ const isSafeCreateRefCallbackCurrentWrite = (referenceNode, accessedPropertyPath, targetPropertyPath, scopes) => {
32478
+ if (accessedPropertyPath.length !== targetPropertyPath.length + 1 || !pathStartsWith$1(accessedPropertyPath, targetPropertyPath) || accessedPropertyPath[targetPropertyPath.length] !== "current") return false;
32479
+ const memberExpression = collectMemberExpression(referenceNode);
32480
+ const assignment = memberExpression?.parent;
32481
+ if (!memberExpression || !assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.left !== memberExpression) return false;
32482
+ const enclosingFunction = findEnclosingFunction$1(referenceNode);
32483
+ if (!enclosingFunction) return false;
32484
+ if (isInlineIntrinsicRefCallback(enclosingFunction, scopes)) return true;
32485
+ if (!isFunctionLike$1(enclosingFunction) || enclosingFunction.async || enclosingFunction.generator) return false;
32486
+ const callbackFunction = findEnclosingFunction$1(enclosingFunction);
32487
+ if (!callbackFunction || !isInlineIntrinsicRefCallback(callbackFunction, scopes)) return false;
32488
+ const cleanupFunction = findTransparentExpressionRoot(enclosingFunction);
32489
+ const cleanupContainer = cleanupFunction.parent;
32490
+ return Boolean(isNodeOfType(cleanupContainer, "ReturnStatement") && cleanupContainer.argument === cleanupFunction && findEnclosingFunction$1(cleanupContainer) === callbackFunction || isNodeOfType(callbackFunction, "ArrowFunctionExpression") && callbackFunction.body === cleanupFunction);
32491
+ };
32492
+ //#endregion
32493
+ //#region src/plugin/rules/react-builtins/is-value-rendered-in-same-render.ts
32494
+ const isValueRenderedFromOwner = (expressionNode, renderOwner, scopes, visitedSymbolIds, options) => {
32495
+ const expression = findTransparentExpressionRoot(expressionNode);
32496
+ const parent = expression.parent;
32497
+ if (!parent) return false;
32498
+ if (isNodeOfType(parent, "ReturnStatement") && parent.argument === expression) return findEnclosingFunction$1(parent) === renderOwner;
32499
+ if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === expression) return parent === renderOwner;
32500
+ if (isNodeOfType(parent, "JSXFragment") && parent.children.some((child) => child === expression)) return isValueRenderedFromOwner(parent, renderOwner, scopes, visitedSymbolIds, options);
32501
+ if (isNodeOfType(parent, "JSXElement") && parent.children.some((child) => child === expression) && (isProvenIntrinsicJsxElement(parent.openingElement, scopes) || isJsxFragmentElement(parent.openingElement, scopes) || options.doesCustomElementRenderChildren?.(parent.openingElement))) return isValueRenderedFromOwner(parent, renderOwner, scopes, visitedSymbolIds, options);
32502
+ if (isNodeOfType(parent, "JSXExpressionContainer") && parent.expression === expression && parent.parent && (isNodeOfType(parent.parent, "JSXFragment") || isNodeOfType(parent.parent, "JSXElement") && (isProvenIntrinsicJsxElement(parent.parent.openingElement, scopes) || isJsxFragmentElement(parent.parent.openingElement, scopes) || options.doesCustomElementRenderChildren?.(parent.parent.openingElement)))) return isValueRenderedFromOwner(parent.parent, renderOwner, scopes, visitedSymbolIds, options);
32503
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === expression || parent.alternate === expression) || isNodeOfType(parent, "LogicalExpression") && (parent.left === expression || parent.right === expression) || isNodeOfType(parent, "ArrayExpression") && parent.elements.some((element) => element === expression)) return isValueRenderedFromOwner(parent, renderOwner, scopes, visitedSymbolIds, options);
32504
+ if (!isNodeOfType(parent, "VariableDeclarator") || parent.init !== expression || !isNodeOfType(parent.id, "Identifier")) return false;
32505
+ const symbol = scopes.symbolFor(parent.id);
32506
+ if (!symbol || symbol.kind !== "const" || symbol.references.length === 0 || visitedSymbolIds.has(symbol.id)) return false;
32507
+ visitedSymbolIds.add(symbol.id);
32508
+ const isRendered = symbol.references.every((reference) => reference.flag === "read" && isValueRenderedFromOwner(reference.identifier, renderOwner, scopes, visitedSymbolIds, options));
32509
+ visitedSymbolIds.delete(symbol.id);
32510
+ return isRendered;
32511
+ };
32512
+ const isValueRenderedInSameRender = (expressionNode, scopes, options = {}) => isValueRenderedFromOwner(expressionNode, findEnclosingFunction$1(expressionNode), scopes, /* @__PURE__ */ new Set(), options);
32513
+ //#endregion
32514
+ //#region src/plugin/rules/react-builtins/is-create-ref-result-write-only.ts
32515
+ const pathStartsWith = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32516
+ const pathsOverlap = (firstPath, secondPath) => pathStartsWith(firstPath, secondPath) || pathStartsWith(secondPath, firstPath);
32517
+ const isClosedNoopFunction = (node) => isFunctionLike$1(node) && isNodeOfType(node.body, "BlockStatement") && node.body.body.length === 0;
32518
+ const isProvenReactCall = (callExpression, expectedName, scopes) => isReactApiCall(callExpression, expectedName, scopes, { resolveNamedAliases: true });
32519
+ const objectHasOnlyClosedNoopFunctions = (objectExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32520
+ if (!isNodeOfType(objectExpression, "ObjectExpression")) return false;
32521
+ return objectExpression.properties.every((property) => {
32522
+ if (isNodeOfType(property, "SpreadElement")) {
32523
+ const spreadArgument = findTransparentExpressionRoot(property.argument);
32524
+ if (isNodeOfType(spreadArgument, "ObjectExpression")) return objectHasOnlyClosedNoopFunctions(spreadArgument, scopes, visitedSymbolIds);
32525
+ if (!isNodeOfType(spreadArgument, "Identifier")) return false;
32526
+ const symbol = scopes.symbolFor(spreadArgument);
32527
+ if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.initializer, "ObjectExpression")) return false;
32528
+ visitedSymbolIds.add(symbol.id);
32529
+ const isClosed = objectHasOnlyClosedNoopFunctions(symbol.initializer, scopes, visitedSymbolIds);
32530
+ visitedSymbolIds.delete(symbol.id);
32531
+ return isClosed;
32532
+ }
32533
+ if (!isNodeOfType(property, "Property")) return false;
32534
+ return !isFunctionLike$1(property.value) || isClosedNoopFunction(property.value);
32535
+ });
32536
+ };
32537
+ const getValuePathPropertyName = (memberExpression) => {
32538
+ const propertyName = getStaticPropertyName(memberExpression);
32539
+ if (propertyName) return propertyName;
32540
+ return memberExpression.computed && isNodeOfType(memberExpression.property, "Literal") && typeof memberExpression.property.value === "number" && Number.isSafeInteger(memberExpression.property.value) && memberExpression.property.value >= 0 ? String(memberExpression.property.value) : null;
32541
+ };
32542
+ const collectMemberAccess = (identifier) => {
32543
+ const propertyPath = [];
32544
+ let expression = findTransparentExpressionRoot(identifier);
32545
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32546
+ const propertyName = getValuePathPropertyName(expression.parent);
32547
+ if (!propertyName) return null;
32548
+ propertyPath.push(propertyName);
32549
+ expression = findTransparentExpressionRoot(expression.parent);
32550
+ }
32551
+ return {
32552
+ expression,
32553
+ propertyPath
32554
+ };
32555
+ };
32556
+ const getEnvironment = (program, filename, state) => {
32557
+ const cached = state.environmentsByProgram.get(program);
32558
+ if (cached) return cached;
32559
+ const environment = {
32560
+ filename,
32561
+ program,
32562
+ scopes: analyzeScopes(program)
32563
+ };
32564
+ state.environmentsByProgram.set(program, environment);
32565
+ return environment;
32566
+ };
32567
+ const unwrapProvenReactHocFunction = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32568
+ if (!node) return null;
32569
+ const current = findTransparentExpressionRoot(node);
32570
+ if (isFunctionLike$1(current)) return current;
32571
+ if (isNodeOfType(current, "Identifier")) {
32572
+ const symbol = scopes.symbolFor(current);
32573
+ if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer || hasSymbolWriteBefore(symbol, current, scopes)) return null;
32574
+ visitedSymbolIds.add(symbol.id);
32575
+ return unwrapProvenReactHocFunction(symbol.initializer, scopes, visitedSymbolIds);
32576
+ }
32577
+ if (!isNodeOfType(current, "CallExpression")) return null;
32578
+ if (!isProvenReactCall(current, "memo", scopes) && !isProvenReactCall(current, "forwardRef", scopes)) return null;
32579
+ const firstArgument = current.arguments[0];
32580
+ if (!firstArgument || isNodeOfType(firstArgument, "SpreadElement")) return null;
32581
+ return unwrapProvenReactHocFunction(firstArgument, scopes, visitedSymbolIds);
32582
+ };
32583
+ const isForwardRefValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32584
+ const current = findTransparentExpressionRoot(node);
32585
+ if (isNodeOfType(current, "Identifier")) {
32586
+ const symbol = scopes.symbolFor(current);
32587
+ if (!symbol || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, current, scopes)) return false;
32588
+ visitedSymbolIds.add(symbol.id);
32589
+ return isForwardRefValue(symbol.initializer, scopes, visitedSymbolIds);
32590
+ }
32591
+ if (!isNodeOfType(current, "CallExpression")) return false;
32592
+ if (isProvenReactCall(current, "forwardRef", scopes)) return true;
32593
+ if (!isProvenReactCall(current, "memo", scopes)) return false;
32594
+ const firstArgument = current.arguments[0];
32595
+ if (!firstArgument || isNodeOfType(firstArgument, "SpreadElement")) return false;
32596
+ return isForwardRefValue(firstArgument, scopes, visitedSymbolIds);
32597
+ };
32598
+ const functionFromExport = (resolved, state) => {
32599
+ const environment = getEnvironment(resolved.programNode, resolved.filePath, state);
32600
+ const functionNode = unwrapProvenReactHocFunction(resolved.exportedNode, environment.scopes);
32601
+ if (!functionNode) return null;
32602
+ return {
32603
+ environment,
32604
+ functionNode,
32605
+ isForwardRef: isForwardRefValue(resolved.exportedNode, environment.scopes)
32606
+ };
32607
+ };
32608
+ const resolveFunctionValue = (identifier, environment, state) => {
32609
+ if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
32610
+ const symbol = environment.scopes.symbolFor(identifier);
32611
+ if (symbol && symbol.kind !== "import") {
32612
+ if (hasSymbolWriteBefore(symbol, identifier, environment.scopes)) return null;
32613
+ const functionNode = unwrapProvenReactHocFunction(symbol.initializer, environment.scopes);
32614
+ if (!functionNode) return null;
32615
+ return {
32616
+ environment,
32617
+ functionNode,
32618
+ isForwardRef: Boolean(symbol.initializer && isForwardRefValue(symbol.initializer, environment.scopes))
32619
+ };
32620
+ }
32621
+ const binding = getImportBindingForName(identifier, identifier.name);
32622
+ if (!binding || binding.isNamespace || binding.exportedName === null) return null;
32623
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, binding.source, binding.exportedName);
32624
+ return resolved ? functionFromExport(resolved, state) : null;
32625
+ };
32626
+ const resolveJsxFunctionValue = (elementName, environment, state) => {
32627
+ if (isNodeOfType(elementName, "JSXIdentifier")) return resolveFunctionValue(elementName, environment, state);
32628
+ if (!isNodeOfType(elementName, "JSXMemberExpression") || !isNodeOfType(elementName.object, "JSXIdentifier")) return null;
32629
+ const namespaceBinding = getImportBindingForName(elementName.object, elementName.object.name);
32630
+ if (!namespaceBinding?.isNamespace) return null;
32631
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, namespaceBinding.source, elementName.property.name);
32632
+ return resolved ? functionFromExport(resolved, state) : null;
32633
+ };
32634
+ const isProvenReactClassValue = (node, environment, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
32635
+ const current = findTransparentExpressionRoot(node);
32636
+ if (isNodeOfType(current, "Identifier")) {
32637
+ const symbol = environment.scopes.symbolFor(current);
32638
+ if (!symbol || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, current, environment.scopes)) return false;
32639
+ visitedSymbolIds.add(symbol.id);
32640
+ return isProvenReactClassValue(symbol.initializer, environment, visitedSymbolIds);
32641
+ }
32642
+ return isProvenReactClassComponent(current, environment.scopes);
32643
+ };
32644
+ const isProvenClassComponentIdentifier = (identifier, environment, state) => {
32645
+ if (!isNodeOfType(identifier, "JSXIdentifier")) return false;
32646
+ const symbol = environment.scopes.symbolFor(identifier);
32647
+ if (symbol && symbol.kind !== "import") {
32648
+ if (hasSymbolWriteBefore(symbol, identifier, environment.scopes)) return false;
32649
+ return Boolean(symbol.initializer && isProvenReactClassValue(symbol.initializer, environment));
32650
+ }
32651
+ const binding = getImportBindingForName(identifier, identifier.name);
32652
+ if (!binding || binding.isNamespace || binding.exportedName === null) return false;
32653
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, binding.source, binding.exportedName);
32654
+ if (!resolved) return false;
32655
+ const resolvedEnvironment = getEnvironment(resolved.programNode, resolved.filePath, state);
32656
+ return isProvenReactClassValue(resolved.exportedNode, resolvedEnvironment);
32657
+ };
32658
+ const resolveClassValue = (node, environment) => {
32659
+ if (!node) return null;
32660
+ const classNode = findTransparentExpressionRoot(node);
32661
+ if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || !isProvenReactClassValue(classNode, environment)) return null;
32662
+ return {
32663
+ classNode,
32664
+ environment
32665
+ };
32666
+ };
32667
+ const resolveJsxClassValue = (elementName, environment, state) => {
32668
+ if (!isNodeOfType(elementName, "JSXIdentifier")) return null;
32669
+ const symbol = environment.scopes.symbolFor(elementName);
32670
+ if (symbol && symbol.kind !== "import") {
32671
+ if (hasSymbolWriteBefore(symbol, elementName, environment.scopes)) return null;
32672
+ return resolveClassValue(symbol.initializer, environment);
32673
+ }
32674
+ const binding = getImportBindingForName(elementName, elementName.name);
32675
+ if (!binding || binding.isNamespace || binding.exportedName === null) return null;
32676
+ const resolved = resolveCrossFileValueExportWithFilePath(environment.filename, binding.source, binding.exportedName);
32677
+ if (!resolved) return null;
32678
+ const resolvedEnvironment = getEnvironment(resolved.programNode, resolved.filePath, state);
32679
+ return resolveClassValue(resolved.exportedNode, resolvedEnvironment);
32680
+ };
32681
+ const findOwnedSymbolValue = (expressionNode, initialPropertyPath, environment) => {
32682
+ const propertyPath = [...initialPropertyPath];
32683
+ let expression = findTransparentExpressionRoot(expressionNode);
32684
+ while (expression.parent) {
32685
+ const parent = expression.parent;
32686
+ if (isNodeOfType(parent, "ArrayExpression")) {
32687
+ if (parent.elements.some((element) => element && isNodeOfType(element, "SpreadElement"))) return null;
32688
+ const elementIndex = parent.elements.findIndex((element) => element === expression);
32689
+ if (elementIndex < 0) return null;
32690
+ propertyPath.unshift(String(elementIndex));
32691
+ expression = findTransparentExpressionRoot(parent);
32692
+ continue;
32693
+ }
32694
+ if (isNodeOfType(parent, "Property") && parent.value === expression) {
32695
+ const propertyName = getStaticPropertyKeyName(parent, { allowComputedString: true });
32696
+ const objectExpression = parent.parent;
32697
+ if (!propertyName || !objectExpression || !objectHasOnlyClosedNoopFunctions(objectExpression, environment.scopes)) return null;
32698
+ propertyPath.unshift(propertyName);
32699
+ expression = findTransparentExpressionRoot(objectExpression);
32700
+ continue;
32701
+ }
32702
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === expression || parent.alternate === expression) || isNodeOfType(parent, "LogicalExpression") && (parent.left === expression || parent.right === expression)) {
32703
+ expression = findTransparentExpressionRoot(parent);
32704
+ continue;
32705
+ }
32706
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === expression && isNodeOfType(parent.id, "Identifier")) {
32707
+ const symbol = environment.scopes.symbolFor(parent.id);
32708
+ if (!symbol || symbol.kind !== "const") return null;
32709
+ return {
32710
+ environment,
32711
+ propertyPath,
32712
+ symbol
32713
+ };
32714
+ }
32715
+ if (isNodeOfType(parent, "AssignmentExpression") && parent.operator === "=" && parent.right === expression && isNodeOfType(parent.left, "MemberExpression")) {
32716
+ const assignedPropertyPath = [];
32717
+ let assignedExpression = parent.left;
32718
+ while (isNodeOfType(assignedExpression, "MemberExpression")) {
32719
+ const propertyName = getValuePathPropertyName(assignedExpression);
32720
+ if (!propertyName) return null;
32721
+ assignedPropertyPath.unshift(propertyName);
32722
+ assignedExpression = findTransparentExpressionRoot(assignedExpression.object);
32723
+ }
32724
+ if (!isNodeOfType(assignedExpression, "Identifier")) return null;
32725
+ const symbol = environment.scopes.symbolFor(assignedExpression);
32726
+ if (!symbol || symbol.kind !== "const") return null;
32727
+ return {
32728
+ environment,
32729
+ originWriteReference: assignedExpression,
32730
+ propertyPath: [...assignedPropertyPath, ...propertyPath],
32731
+ symbol
32732
+ };
32733
+ }
32734
+ return null;
32735
+ }
32736
+ return null;
32737
+ };
32738
+ const analyzePatternPath = (pattern, propertyPath, environment, state, remainingDepth) => {
32739
+ if (isNodeOfType(pattern, "AssignmentPattern")) return analyzePatternPath(pattern.left, propertyPath, environment, state, remainingDepth);
32740
+ if (isNodeOfType(pattern, "Identifier")) {
32741
+ const symbol = environment.scopes.symbolFor(pattern);
32742
+ return Boolean(symbol && analyzeSymbolValuePath({
32743
+ environment,
32744
+ propertyPath,
32745
+ symbol
32746
+ }, state, remainingDepth - 1));
32747
+ }
32748
+ if (!isNodeOfType(pattern, "ObjectPattern") || propertyPath.length === 0) return false;
32749
+ const [firstPropertyName, ...remainingPropertyPath] = propertyPath;
32750
+ for (const property of pattern.properties) {
32751
+ if (!isNodeOfType(property, "Property")) continue;
32752
+ const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
32753
+ if (!propertyName) return false;
32754
+ if (propertyName !== firstPropertyName) continue;
32755
+ return analyzePatternPath(property.value, remainingPropertyPath, environment, state, remainingDepth);
32756
+ }
32757
+ const restProperty = pattern.properties.find((property) => isNodeOfType(property, "RestElement"));
32758
+ return restProperty ? analyzePatternPath(restProperty.argument, propertyPath, environment, state, remainingDepth) : true;
32759
+ };
32760
+ const isIntrinsicReactElementFactoryCall = (callExpression, environment) => {
32761
+ const element = callExpression.arguments[0];
32762
+ if (!element || isNodeOfType(element, "SpreadElement")) return false;
32763
+ if (isProvenReactCall(callExpression, "createElement", environment.scopes)) return isNodeOfType(element, "Literal") && typeof element.value === "string";
32764
+ if (!isProvenReactCall(callExpression, "cloneElement", environment.scopes)) return false;
32765
+ if (!isNodeOfType(element, "JSXElement")) return false;
32766
+ return isProvenIntrinsicJsxElement(element.openingElement, environment.scopes);
32767
+ };
32768
+ const isIntrinsicReactElementRefProperty = (expression, propertyPath, environment, state, remainingDepth) => {
32769
+ if (propertyPath.length > 0) return false;
32770
+ const property = expression.parent;
32771
+ if (!property || !isNodeOfType(property, "Property") || property.value !== expression) return false;
32772
+ if (getStaticPropertyKeyName(property, { allowComputedString: true }) !== "ref") return false;
32773
+ const props = property.parent;
32774
+ if (!props || !isNodeOfType(props, "ObjectExpression")) return false;
32775
+ const callExpression = props.parent;
32776
+ if (!callExpression || !isNodeOfType(callExpression, "CallExpression") || callExpression.arguments[1] !== props || !isIntrinsicReactElementFactoryCall(callExpression, environment) || !isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) return false;
32777
+ return true;
32778
+ };
32779
+ const analyzeFunctionInput = (resolvedFunction, parameterIndex, propertyPath, state, remainingDepth) => {
32780
+ if (remainingDepth <= 0 || !isFunctionLike$1(resolvedFunction.functionNode) || resolvedFunction.functionNode.async || resolvedFunction.functionNode.generator) return false;
32781
+ const parameter = resolvedFunction.functionNode.params[parameterIndex];
32782
+ return Boolean(parameter && analyzePatternPath(parameter, propertyPath, resolvedFunction.environment, state, remainingDepth));
32783
+ };
32784
+ const isThisPropsChildren = (node) => {
32785
+ if (!isNodeOfType(node, "MemberExpression") || getStaticPropertyName(node) !== "children") return false;
32786
+ const propsMember = findTransparentExpressionRoot(node.object);
32787
+ return Boolean(isNodeOfType(propsMember, "MemberExpression") && getStaticPropertyName(propsMember) === "props" && isNodeOfType(findTransparentExpressionRoot(propsMember.object), "ThisExpression"));
32788
+ };
32789
+ const isProvenReactContextProvider = (openingElement, environment) => {
32790
+ if (!isNodeOfType(openingElement, "JSXOpeningElement") || !isNodeOfType(openingElement.name, "JSXMemberExpression") || openingElement.name.property.name !== "Provider" || !isNodeOfType(openingElement.name.object, "JSXIdentifier")) return false;
32791
+ const contextSymbol = environment.scopes.symbolFor(openingElement.name.object);
32792
+ return Boolean(contextSymbol?.kind === "const" && contextSymbol.initializer && isNodeOfType(contextSymbol.initializer, "CallExpression") && isProvenReactCall(contextSymbol.initializer, "createContext", environment.scopes));
32793
+ };
32794
+ const isValueRenderedForEnvironment = (expression, environment, state, remainingDepth) => isValueRenderedInSameRender(expression, environment.scopes, { doesCustomElementRenderChildren: (openingElement) => doesCustomElementRenderChildren(openingElement, environment, state, remainingDepth - 1) });
32795
+ const doesFunctionComponentRenderChildren = (resolvedFunction, state, remainingDepth) => {
32796
+ const { functionNode } = resolvedFunction;
32797
+ if (remainingDepth <= 0 || state.activeRenderedChildrenComponents.has(functionNode) || !isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "FunctionDeclaration") || functionNode.async || functionNode.generator) return false;
32798
+ const childrenReferences = collectFunctionChildrenReferences(functionNode, resolvedFunction.environment.scopes);
32799
+ if (!childrenReferences) return false;
32800
+ state.activeRenderedChildrenComponents.add(functionNode);
32801
+ const isRendered = childrenReferences.every((childrenReference) => isValueRenderedForEnvironment(childrenReference, resolvedFunction.environment, state, remainingDepth));
32802
+ state.activeRenderedChildrenComponents.delete(functionNode);
32803
+ return isRendered;
32804
+ };
32805
+ const doesClassComponentRenderChildren = (resolvedClass, state, remainingDepth) => {
32806
+ const { classNode, environment } = resolvedClass;
32807
+ if (remainingDepth <= 0 || state.activeRenderedChildrenComponents.has(classNode) || !isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression")) return false;
32808
+ const renderMethod = classNode.body.body.find((element) => isNodeOfType(element, "MethodDefinition") && !element.computed && isNodeOfType(element.key, "Identifier") && element.key.name === "render");
32809
+ if (!renderMethod || !isNodeOfType(renderMethod, "MethodDefinition")) return false;
32810
+ const childrenReads = [];
32811
+ walkAst(classNode, (node) => {
32812
+ if (isThisPropsChildren(node)) childrenReads.push(node);
32813
+ });
32814
+ if (childrenReads.length === 0) return false;
32815
+ state.activeRenderedChildrenComponents.add(classNode);
32816
+ const isRendered = childrenReads.every((childrenRead) => findEnclosingFunction$1(childrenRead) === renderMethod.value && isValueRenderedForEnvironment(childrenRead, environment, state, remainingDepth));
32817
+ state.activeRenderedChildrenComponents.delete(classNode);
32818
+ return isRendered;
32819
+ };
32820
+ const doesCustomElementRenderChildren = (openingElement, environment, state, remainingDepth) => {
32821
+ if (remainingDepth <= 0 || !isNodeOfType(openingElement, "JSXOpeningElement")) return false;
32822
+ if (isProvenReactContextProvider(openingElement, environment)) return true;
32823
+ const resolvedFunction = resolveJsxFunctionValue(openingElement.name, environment, state);
32824
+ if (resolvedFunction) return doesFunctionComponentRenderChildren(resolvedFunction, state, remainingDepth);
32825
+ const resolvedClass = resolveJsxClassValue(openingElement.name, environment, state);
32826
+ return Boolean(resolvedClass && doesClassComponentRenderChildren(resolvedClass, state, remainingDepth));
32827
+ };
32828
+ const analyzeJsxAttributeUse = (attribute, propertyPath, environment, state, remainingDepth) => {
32829
+ const attributeName = getJsxAttributeName(attribute.name);
32830
+ const openingElement = attribute.parent;
32831
+ if (!attributeName || !openingElement || !isNodeOfType(openingElement, "JSXOpeningElement")) return false;
32832
+ const jsxElement = openingElement.parent;
32833
+ if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement") || !isValueRenderedForEnvironment(jsxElement, environment, state, remainingDepth)) return false;
32834
+ if (isProvenIntrinsicJsxElement(openingElement, environment.scopes)) return attributeName === "ref" && propertyPath.length === 0;
32835
+ if (isNodeOfType(openingElement.name, "JSXIdentifier") && attributeName === "ref" && propertyPath.length === 0 && isProvenClassComponentIdentifier(openingElement.name, environment, state)) return true;
32836
+ const resolvedFunction = resolveJsxFunctionValue(openingElement.name, environment, state);
32837
+ if (!resolvedFunction) return false;
32838
+ if (attributeName === "ref" && resolvedFunction.isForwardRef) return analyzeFunctionInput(resolvedFunction, 1, propertyPath, state, remainingDepth);
32839
+ return analyzeFunctionInput(resolvedFunction, 0, [attributeName, ...propertyPath], state, remainingDepth);
32840
+ };
32841
+ const analyzeCallArgumentUse = (callExpression, argumentExpression, propertyPath, environment, state, remainingDepth) => {
32842
+ const argumentIndex = callExpression.arguments.findIndex((argument) => argument === argumentExpression);
32843
+ if (argumentIndex < 0) return false;
32844
+ if (argumentIndex === 0 && isNodeOfType(callExpression.callee, "Identifier") && callExpression.callee.name === "Boolean" && environment.scopes.isGlobalReference(callExpression.callee)) return true;
32845
+ if (argumentIndex === 0 && isProvenReactCall(callExpression, "useImperativeHandle", environment.scopes)) return propertyPath.length === 0;
32846
+ if (argumentIndex === 1 && propertyPath.length === 1 && propertyPath[0] === "ref" && isIntrinsicReactElementFactoryCall(callExpression, environment) && isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) return true;
32847
+ const inlineCallee = findTransparentExpressionRoot(callExpression.callee);
32848
+ if (isFunctionLike$1(inlineCallee)) {
32849
+ if (functionContainsReactRenderOutput(inlineCallee, environment.scopes) && !isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) return false;
32850
+ return analyzeFunctionInput({
32851
+ environment,
32852
+ functionNode: inlineCallee,
32853
+ isForwardRef: false
32854
+ }, argumentIndex, propertyPath, state, remainingDepth);
32855
+ }
32856
+ if (!isNodeOfType(callExpression.callee, "Identifier")) return false;
32857
+ const resolvedFunction = resolveFunctionValue(callExpression.callee, environment, state);
32858
+ return Boolean(resolvedFunction && (!functionContainsReactRenderOutput(resolvedFunction.functionNode, resolvedFunction.environment.scopes) || isValueRenderedForEnvironment(callExpression, environment, state, remainingDepth)) && analyzeFunctionInput(resolvedFunction, argumentIndex, propertyPath, state, remainingDepth));
32859
+ };
32860
+ const isIntrinsicJsxSpreadRefUse = (expression, propertyPath, environment, state, remainingDepth) => {
32861
+ let spreadAttribute;
32862
+ if (propertyPath.length === 1 && propertyPath[0] === "ref") spreadAttribute = expression.parent;
32863
+ else if (propertyPath.length === 0) {
32864
+ const property = expression.parent;
32865
+ if (!property || !isNodeOfType(property, "Property") || property.value !== expression || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "ref") return false;
32866
+ spreadAttribute = property.parent?.parent;
32867
+ }
32868
+ if (!spreadAttribute || !isNodeOfType(spreadAttribute, "JSXSpreadAttribute")) return false;
32869
+ const openingElement = spreadAttribute.parent;
32870
+ if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement")) return false;
32871
+ const jsxElement = openingElement.parent;
32872
+ return Boolean(isProvenIntrinsicJsxElement(openingElement, environment.scopes) && jsxElement && isNodeOfType(jsxElement, "JSXElement") && isValueRenderedForEnvironment(jsxElement, environment, state, remainingDepth));
32873
+ };
32874
+ const analyzeValueUse = (expressionNode, propertyPath, environment, state, remainingDepth) => {
32875
+ const expression = findTransparentExpressionRoot(expressionNode);
32876
+ const parent = expression.parent;
32877
+ if (!parent) return false;
32878
+ if (isNodeOfType(parent, "ExpressionStatement")) return true;
32879
+ if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "void" && parent.argument === expression) return true;
32880
+ if (propertyPath.length === 0 && isNodeOfType(parent, "BinaryExpression") && (parent.operator === "===" || parent.operator === "!==" || parent.operator === "==" || parent.operator === "!=")) {
32881
+ const comparedExpression = parent.left === expression ? parent.right : parent.left;
32882
+ return Boolean(isNodeOfType(expression, "Identifier") && isNodeOfType(comparedExpression, "Identifier") && environment.scopes.symbolFor(expression)?.id === environment.scopes.symbolFor(comparedExpression)?.id);
32883
+ }
32884
+ if (isIntrinsicJsxSpreadRefUse(expression, propertyPath, environment, state, remainingDepth)) return true;
32885
+ if (isIntrinsicReactElementRefProperty(expression, propertyPath, environment, state, remainingDepth)) return true;
32886
+ if (propertyPath.length > 0 && (isNodeOfType(parent, "LogicalExpression") && parent.left === expression || isNodeOfType(parent, "ConditionalExpression") && parent.test === expression || isNodeOfType(parent, "IfStatement") && parent.test === expression || isNodeOfType(parent, "UnaryExpression") && parent.operator === "!" && parent.argument === expression)) return true;
32887
+ if (isNodeOfType(parent, "JSXExpressionContainer") && parent.expression === expression) {
32888
+ const attribute = parent.parent;
32889
+ return Boolean(attribute && isNodeOfType(attribute, "JSXAttribute") && analyzeJsxAttributeUse(attribute, propertyPath, environment, state, remainingDepth));
32890
+ }
32891
+ if (isNodeOfType(parent, "CallExpression")) return analyzeCallArgumentUse(parent, expression, propertyPath, environment, state, remainingDepth);
32892
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === expression && !isNodeOfType(parent.id, "Identifier")) return analyzePatternPath(parent.id, propertyPath, environment, state, remainingDepth);
32893
+ const propagatedValue = findOwnedSymbolValue(expression, propertyPath, environment);
32894
+ return Boolean(propagatedValue && analyzeSymbolValuePath(propagatedValue, state, remainingDepth - 1));
32895
+ };
32896
+ const getFunctionBindingIdentifier = (functionNode) => {
32897
+ const parent = functionNode.parent;
32898
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
32899
+ if ((isNodeOfType(functionNode, "FunctionDeclaration") || isNodeOfType(functionNode, "FunctionExpression")) && functionNode.id) return functionNode.id;
32900
+ return null;
32901
+ };
32902
+ const isSynchronouslyInvokedLocalFunction = (functionNode, callerFunction, scopes) => {
32903
+ if (!functionNode || !callerFunction || !isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator) return false;
32904
+ const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
32905
+ if (!bindingIdentifier) return false;
32906
+ if (((isNodeOfType(functionNode, "FunctionDeclaration") || isNodeOfType(functionNode, "FunctionExpression")) && functionNode.id === bindingIdentifier ? findEnclosingFunction$1(functionNode) : findEnclosingFunction$1(bindingIdentifier)) !== callerFunction) return false;
32907
+ const symbol = isNodeOfType(functionNode, "FunctionDeclaration") ? scopes.ownScopeFor(callerFunction)?.symbolsByName.get(bindingIdentifier.name) : scopes.symbolFor(bindingIdentifier);
32908
+ return Boolean(symbol && symbol.references.length > 0 && symbol.references.every((reference) => {
32909
+ const referenceExpression = findTransparentExpressionRoot(reference.identifier);
32910
+ const callExpression = referenceExpression.parent;
32911
+ return Boolean(callExpression && isNodeOfType(callExpression, "CallExpression") && callExpression.callee === referenceExpression && findEnclosingFunction$1(callExpression) === callerFunction && isValueRenderedInSameRender(callExpression, scopes));
32912
+ }));
32913
+ };
32914
+ const analyzeSymbolValuePath = (valuePath, state, remainingDepth) => {
32915
+ if (remainingDepth <= 0) return false;
32916
+ const activePathKey = `${valuePath.environment.filename}:${valuePath.symbol.id}:${valuePath.propertyPath.join(".")}`;
32917
+ if (state.activePaths.has(activePathKey)) return false;
32918
+ state.activePaths.add(activePathKey);
32919
+ const bindingFunction = findEnclosingFunction$1(valuePath.symbol.bindingIdentifier);
32920
+ const result = valuePath.symbol.references.every((reference) => {
32921
+ if (reference.identifier === valuePath.originWriteReference) return true;
32922
+ const referenceParent = reference.identifier.parent;
32923
+ if (referenceParent && isNodeOfType(referenceParent, "Property") && !referenceParent.computed && referenceParent.key === reference.identifier && referenceParent.value !== reference.identifier) return true;
32924
+ const memberAccess = collectMemberAccess(reference.identifier);
32925
+ if (!memberAccess) return false;
32926
+ if (!pathsOverlap(memberAccess.propertyPath, valuePath.propertyPath)) return true;
32927
+ const referenceFunction = findEnclosingFunction$1(reference.identifier);
32928
+ if (referenceFunction !== bindingFunction && !isSynchronouslyInvokedLocalFunction(referenceFunction, bindingFunction, valuePath.environment.scopes)) {
32929
+ if (isSafeCreateRefCallbackCurrentWrite(reference.identifier, memberAccess.propertyPath, valuePath.propertyPath, valuePath.environment.scopes)) return true;
32930
+ return false;
32931
+ }
32932
+ if (reference.flag !== "read") return false;
32933
+ if (pathStartsWith(memberAccess.propertyPath, valuePath.propertyPath)) {
32934
+ if (memberAccess.propertyPath.length > valuePath.propertyPath.length) {
32935
+ const parent = memberAccess.expression.parent;
32936
+ return Boolean(memberAccess.propertyPath.length === valuePath.propertyPath.length + 1 && memberAccess.propertyPath[valuePath.propertyPath.length] === "current" && parent && isNodeOfType(parent, "UnaryExpression") && parent.operator === "void" && parent.argument === memberAccess.expression);
32937
+ }
32938
+ return analyzeValueUse(memberAccess.expression, [], valuePath.environment, state, remainingDepth);
32939
+ }
32940
+ return analyzeValueUse(memberAccess.expression, valuePath.propertyPath.slice(memberAccess.propertyPath.length), valuePath.environment, state, remainingDepth);
32941
+ });
32942
+ state.activePaths.delete(activePathKey);
32943
+ return result;
32944
+ };
32945
+ const isCreateRefResultWriteOnly = (createRefCall, filename, scopes) => {
32946
+ if (!filename) return false;
32947
+ const program = findProgramRoot(createRefCall);
32948
+ if (!program) return false;
32949
+ const environment = {
32950
+ filename,
32951
+ program,
32952
+ scopes
32953
+ };
32954
+ const state = {
32955
+ activePaths: /* @__PURE__ */ new Set(),
32956
+ activeRenderedChildrenComponents: /* @__PURE__ */ new WeakSet(),
32957
+ environmentsByProgram: new WeakMap([[program, environment]])
32958
+ };
32959
+ const ownedValue = findOwnedSymbolValue(createRefCall, [], environment);
32960
+ return ownedValue ? analyzeSymbolValuePath(ownedValue, state, 12) : analyzeValueUse(createRefCall, [], environment, state, 12);
32961
+ };
32962
+ //#endregion
31991
32963
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
31992
- 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.";
31993
- const isUseMemoCallbackArgument = (functionNode) => {
32964
+ const MESSAGE$31 = "`createRef()` may escape or be observed beyond the render that created it, so a later render can replace the ref object and detach the observed one. Hoist a `useRef()` call to the component's unconditional top level instead.";
32965
+ const isUseMemoCallbackArgument = (functionNode, scopes) => {
31994
32966
  const parent = functionNode.parent;
31995
32967
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
31996
32968
  if (parent.arguments?.[0] !== functionNode) return false;
31997
- return isReactFunctionCall(parent, "useMemo");
32969
+ return isReactApiCall(parent, "useMemo", scopes, { resolveNamedAliases: true });
31998
32970
  };
31999
- const findEnclosingRenderFunction = (node) => {
32971
+ const findEnclosingRenderFunction = (node, scopes) => {
32000
32972
  let enclosingFunction = findEnclosingFunction$1(node);
32001
- while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
32973
+ while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction, scopes)) enclosingFunction = findEnclosingFunction$1(enclosingFunction);
32002
32974
  return enclosingFunction;
32003
32975
  };
32004
32976
  const noCreateRefInFunctionComponent = defineRule({
@@ -32007,16 +32979,17 @@ const noCreateRefInFunctionComponent = defineRule({
32007
32979
  severity: "warn",
32008
32980
  recommendation: "Replace `createRef()` with the `useRef()` hook inside function components and hooks. `createRef` is only for class components.",
32009
32981
  create: (context) => ({ CallExpression(node) {
32010
- if (!isReactFunctionCall(node, "createRef")) return;
32011
- if (isNodeOfType(node.callee, "Identifier")) {
32012
- const symbol = context.scopes.symbolFor(node.callee);
32013
- if (symbol && symbol.kind !== "import") return;
32014
- }
32015
- const enclosingFunction = findEnclosingRenderFunction(node);
32982
+ if (!isReactApiCall(node, "createRef", context.scopes, {
32983
+ allowGlobalReactNamespace: true,
32984
+ allowUnboundBareCalls: true,
32985
+ resolveNamedAliases: true
32986
+ })) return;
32987
+ const enclosingFunction = findEnclosingRenderFunction(node, context.scopes);
32016
32988
  if (!enclosingFunction) return;
32017
32989
  const displayName = componentOrHookDisplayNameForFunction(enclosingFunction);
32018
32990
  if (!displayName) return;
32019
32991
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes, context.cfg))) return;
32992
+ if (isCreateRefResultWriteOnly(node, context.filename, context.scopes)) return;
32020
32993
  context.report({
32021
32994
  node,
32022
32995
  message: MESSAGE$31
@@ -32541,6 +33514,28 @@ const noDarkModeGlow = defineRule({
32541
33514
  });
32542
33515
  //#endregion
32543
33516
  //#region src/plugin/rules/architecture/no-default-props.ts
33517
+ const hasReachingWriteOnAliasPath = (receiver, context) => {
33518
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
33519
+ let reference = receiver;
33520
+ let symbol = context.scopes.symbolFor(reference);
33521
+ while (symbol) {
33522
+ if (visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, reference, context.scopes)) return true;
33523
+ visitedSymbolIds.add(symbol.id);
33524
+ if (symbol.kind !== "const" || !symbol.initializer) return false;
33525
+ const initializer = stripParenExpression(symbol.initializer);
33526
+ if (!isNodeOfType(initializer, "Identifier")) return false;
33527
+ reference = initializer;
33528
+ symbol = context.scopes.symbolFor(reference);
33529
+ }
33530
+ return false;
33531
+ };
33532
+ const isStableClassReceiver = (receiver, context) => {
33533
+ const symbol = resolveConstIdentifierAlias(receiver, context.scopes);
33534
+ if (!symbol || hasReachingWriteOnAliasPath(receiver, context)) return false;
33535
+ if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return true;
33536
+ const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
33537
+ return Boolean(symbol.kind === "const" && isNodeOfType(initializer, "ClassExpression"));
33538
+ };
32544
33539
  const noDefaultProps = defineRule({
32545
33540
  id: "no-default-props",
32546
33541
  title: "defaultProps removed in React 19",
@@ -32556,6 +33551,7 @@ const noDefaultProps = defineRule({
32556
33551
  if (!isNodeOfType(left.property, "Identifier") || left.property.name !== "defaultProps") return;
32557
33552
  if (!isNodeOfType(left.object, "Identifier")) return;
32558
33553
  if (!isUppercaseName(left.object.name)) return;
33554
+ if (isStableClassReceiver(left.object, context)) return;
32559
33555
  context.report({
32560
33556
  node: left,
32561
33557
  message: `${left.object.name}.defaultProps stops applying in React 19, so your users see missing defaults. Set them in the destructured props parameter instead, like \`function ${left.object.name}({ size = "md" })\`.`
@@ -38195,33 +39191,6 @@ const noLegacyClassLifecycles = defineRule({
38195
39191
  }
38196
39192
  });
38197
39193
  //#endregion
38198
- //#region src/plugin/utils/is-proven-react-class-component.ts
38199
- const REACT_COMPONENT_CLASS_NAMES = new Set(["Component", "PureComponent"]);
38200
- const isReactComponentClassValue = (node, scopes, visitedClassNodes, visitedSymbolIds) => {
38201
- const expression = stripParenExpression(node);
38202
- if (isNodeOfType(expression, "MemberExpression")) {
38203
- const propertyName = getStaticPropertyName(expression);
38204
- const receiver = stripParenExpression(expression.object);
38205
- return Boolean(propertyName && REACT_COMPONENT_CLASS_NAMES.has(propertyName) && isNodeOfType(receiver, "Identifier") && !hasStaticPropertyWriteBefore(receiver, propertyName, expression, scopes) && isReactNamespaceImport(receiver, scopes));
38206
- }
38207
- if (isNodeOfType(expression, "ClassExpression")) return isProvenReactClassComponent(expression, scopes, visitedClassNodes, visitedSymbolIds);
38208
- if (!isNodeOfType(expression, "Identifier")) return false;
38209
- const symbol = scopes.symbolFor(expression);
38210
- if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, expression, scopes)) return false;
38211
- visitedSymbolIds.add(symbol.id);
38212
- if (isImportedFromReact(symbol)) {
38213
- const importedName = getImportedName(symbol.declarationNode);
38214
- return Boolean(importedName && REACT_COMPONENT_CLASS_NAMES.has(importedName));
38215
- }
38216
- if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes, visitedClassNodes, visitedSymbolIds);
38217
- return Boolean(symbol.kind === "const" && symbol.initializer && isReactComponentClassValue(symbol.initializer, scopes, visitedClassNodes, visitedSymbolIds));
38218
- };
38219
- const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @__PURE__ */ new Set(), visitedSymbolIds = /* @__PURE__ */ new Set()) => {
38220
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression") || visitedClassNodes.has(classNode) || !classNode.superClass) return false;
38221
- visitedClassNodes.add(classNode);
38222
- return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
38223
- };
38224
- //#endregion
38225
39194
  //#region src/plugin/utils/function-contains-proven-react-hook-call.ts
38226
39195
  const functionContainsProvenReactHookCall = (functionNode, scopes) => {
38227
39196
  if (!isFunctionLike$1(functionNode)) return false;
@@ -38685,7 +39654,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
38685
39654
  }
38686
39655
  if (isFunctionInvokedBeforeUsage(methodCallFunction, usageNode, usageBoundary, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes))) return true;
38687
39656
  }
38688
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
39657
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
38689
39658
  if (!bindingIdentifier) return false;
38690
39659
  const symbol = scopes.symbolFor(bindingIdentifier);
38691
39660
  if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
@@ -41228,6 +42197,133 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
41228
42197
  }
41229
42198
  return callbackPropNames.size > 0 ? { callbackPropNames } : null;
41230
42199
  };
42200
+ const isParentPropsContextMerge = (analysis, expression) => {
42201
+ let currentExpression = stripParenExpression(expression);
42202
+ const visitedVariables = /* @__PURE__ */ new Set();
42203
+ while (isNodeOfType(currentExpression, "Identifier")) {
42204
+ const currentReference = getRef(analysis, currentExpression);
42205
+ const currentVariable = currentReference?.resolved;
42206
+ if (!currentReference || !currentVariable || visitedVariables.has(currentVariable) || hasMutableBindingWrite(currentReference)) return false;
42207
+ visitedVariables.add(currentVariable);
42208
+ const definitions = currentVariable.defs.filter((definition) => isNodeOfType(definition.node, "VariableDeclarator"));
42209
+ if (definitions.length !== 1) return false;
42210
+ const declarator = definitions[0]?.node;
42211
+ if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || getDeclarationKind(declarator) !== "const" || !declarator.init) return false;
42212
+ currentExpression = stripParenExpression(declarator.init);
42213
+ }
42214
+ if (!isNodeOfType(currentExpression, "ObjectExpression")) return false;
42215
+ const [contextSpread, propsSpread] = currentExpression.properties ?? [];
42216
+ if (currentExpression.properties?.length !== 2 || !contextSpread || !propsSpread || !isNodeOfType(contextSpread, "SpreadElement") || !isNodeOfType(propsSpread, "SpreadElement")) return false;
42217
+ const propsExpression = stripParenExpression(propsSpread.argument);
42218
+ if (!isNodeOfType(propsExpression, "Identifier")) return false;
42219
+ const propsReference = getRef(analysis, propsExpression);
42220
+ if (!propsReference?.resolved || !isWholePropsObjectReference(analysis, propsReference) || hasMutableBindingWrite(propsReference) || propsReference.resolved.references.some((candidateReference) => candidateReference !== propsReference)) return false;
42221
+ const contextExpression = stripParenExpression(contextSpread.argument);
42222
+ if (!isNodeOfType(contextExpression, "Identifier")) return false;
42223
+ const contextReference = getRef(analysis, contextExpression);
42224
+ if (!contextReference?.resolved || hasMutableBindingWrite(contextReference) || contextReference.resolved.references.some((candidateReference) => !candidateReference.init && candidateReference !== contextReference)) return false;
42225
+ const contextInitializer = contextReference.resolved?.defs.map((definition) => definition.node).find((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
42226
+ if (!contextInitializer || !isNodeOfType(contextInitializer, "VariableDeclarator") || getDeclarationKind(contextInitializer) !== "const" || !contextInitializer.init || !isNodeOfType(contextInitializer.init, "CallExpression")) return false;
42227
+ const contextHook = stripParenExpression(contextInitializer.init.callee);
42228
+ if (!isNodeOfType(contextHook, "Identifier") || !/^use[A-Z].*Context$/.test(contextHook.name)) return false;
42229
+ const contextHookReference = getRef(analysis, contextHook);
42230
+ return Boolean(contextHookReference?.resolved?.defs.some((definition) => definition.type === "ImportBinding"));
42231
+ };
42232
+ const getImmutableParentCallbackPropName = (analysis, expression) => {
42233
+ let currentExpression = stripParenExpression(expression);
42234
+ const visitedVariables = /* @__PURE__ */ new Set();
42235
+ while (isNodeOfType(currentExpression, "Identifier")) {
42236
+ const currentReference = getRef(analysis, currentExpression);
42237
+ const currentVariable = currentReference?.resolved;
42238
+ if (!currentReference || !currentVariable || visitedVariables.has(currentVariable) || hasMutableBindingWrite(currentReference)) return null;
42239
+ visitedVariables.add(currentVariable);
42240
+ const definition = currentVariable.defs.length === 1 ? currentVariable.defs[0] : null;
42241
+ const bindingIdentifier = definition?.name;
42242
+ if (bindingIdentifier && isNodeOfType(bindingIdentifier.parent, "AssignmentPattern")) return null;
42243
+ const definitionNode = definition?.node;
42244
+ if (!definitionNode || !isNodeOfType(definitionNode, "VariableDeclarator")) return getParentCallbackPropName(analysis, currentExpression);
42245
+ if (getDeclarationKind(definitionNode) !== "const" || !definitionNode.init) return null;
42246
+ if (isNodeOfType(definitionNode.id, "ObjectPattern")) return getParentCallbackPropName(analysis, currentExpression) ?? (isParentPropsContextMerge(analysis, definitionNode.init) ? getDestructuredBindingPropertyName(bindingIdentifier ?? currentExpression) : null);
42247
+ if (!isNodeOfType(definitionNode.id, "Identifier")) return null;
42248
+ currentExpression = stripParenExpression(definitionNode.init);
42249
+ if (!isNodeOfType(currentExpression, "Identifier") && !isNodeOfType(currentExpression, "MemberExpression")) return null;
42250
+ }
42251
+ return null;
42252
+ };
42253
+ const objectExpressionPreservesCallbackProperty = (analysis, expression, propertyName) => {
42254
+ const unwrappedExpression = stripParenExpression(expression);
42255
+ if (!isNodeOfType(unwrappedExpression, "ObjectExpression")) return false;
42256
+ let callbackSourceName = null;
42257
+ for (const property of unwrappedExpression.properties ?? []) {
42258
+ if (!isNodeOfType(property, "Property")) return false;
42259
+ const candidatePropertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
42260
+ if (candidatePropertyName === null) return false;
42261
+ if (candidatePropertyName !== propertyName) continue;
42262
+ if (callbackSourceName || property.kind !== "init") return false;
42263
+ if (!isNodeOfType(stripParenExpression(property.value), "Identifier")) return false;
42264
+ callbackSourceName = getImmutableParentCallbackPropName(analysis, property.value);
42265
+ if (!callbackSourceName) return false;
42266
+ }
42267
+ return callbackSourceName === propertyName;
42268
+ };
42269
+ const refCurrentObjectPreservesCallbackProperty = (analysis, expression, propertyName, isReactUseRefCall) => {
42270
+ const unwrappedExpression = stripParenExpression(expression);
42271
+ if (!isNodeOfType(unwrappedExpression, "MemberExpression") || getStaticMemberPropertyName(unwrappedExpression) !== "current") return false;
42272
+ const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(unwrappedExpression.object), isReactUseRefCall);
42273
+ if (!bindingProvenance) return false;
42274
+ const initializer = bindingProvenance.refCall.arguments?.[0];
42275
+ if (!initializer || !objectExpressionPreservesCallbackProperty(analysis, initializer, propertyName)) return false;
42276
+ for (const variable of bindingProvenance.variables) for (const candidateReference of variable.references) {
42277
+ if (candidateReference.init) continue;
42278
+ const identifier = candidateReference.identifier;
42279
+ if (getRefAliasDeclarator(identifier)) continue;
42280
+ const currentMember = getRefMember(identifier);
42281
+ if (!currentMember || getStaticMemberPropertyName(currentMember) !== "current") return false;
42282
+ const currentAssignment = getRefMemberAssignment(identifier);
42283
+ if (currentAssignment) {
42284
+ if (currentAssignment.operator !== "=" || !objectExpressionPreservesCallbackProperty(analysis, currentAssignment.right, propertyName)) return false;
42285
+ continue;
42286
+ }
42287
+ const currentRoot = findTransparentExpressionRoot(currentMember);
42288
+ const currentParent = currentRoot.parent;
42289
+ if (currentParent && isNodeOfType(currentParent, "VariableDeclarator") && currentParent.init === currentRoot && isNodeOfType(currentParent.id, "ObjectPattern")) continue;
42290
+ if (currentParent && isNodeOfType(currentParent, "MemberExpression") && currentParent.object === currentRoot && getStaticMemberPropertyName(currentParent) === propertyName) {
42291
+ const propertyParent = findTransparentExpressionRoot(currentParent).parent;
42292
+ if (!propertyParent || !isNodeOfType(propertyParent, "VariableDeclarator")) return false;
42293
+ continue;
42294
+ }
42295
+ return false;
42296
+ }
42297
+ return true;
42298
+ };
42299
+ const getCommandCallbackPropName = (analysis, expression, isReactUseRefCall) => {
42300
+ const directCallbackName = getImmutableParentCallbackPropName(analysis, expression);
42301
+ if (directCallbackName && COMMAND_PROP_NAME_PATTERN.test(directCallbackName)) return directCallbackName;
42302
+ let currentExpression = stripParenExpression(expression);
42303
+ const visitedVariables = /* @__PURE__ */ new Set();
42304
+ while (isNodeOfType(currentExpression, "Identifier")) {
42305
+ const callbackReference = getRef(analysis, currentExpression);
42306
+ const callbackVariable = callbackReference?.resolved;
42307
+ if (!callbackReference || !callbackVariable || visitedVariables.has(callbackVariable) || hasMutableBindingWrite(callbackReference)) return null;
42308
+ visitedVariables.add(callbackVariable);
42309
+ const definition = callbackVariable.defs.length === 1 ? callbackVariable.defs[0] : null;
42310
+ const declarator = definition?.node;
42311
+ if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || getDeclarationKind(declarator) !== "const" || !declarator.init) return null;
42312
+ const bindingIdentifier = definition?.name;
42313
+ if (bindingIdentifier && isNodeOfType(bindingIdentifier.parent, "AssignmentPattern")) return null;
42314
+ if (isNodeOfType(declarator.id, "ObjectPattern")) {
42315
+ const propertyName = bindingIdentifier ? getDestructuredBindingPropertyName(bindingIdentifier) : null;
42316
+ if (!propertyName || !COMMAND_PROP_NAME_PATTERN.test(propertyName)) return null;
42317
+ return refCurrentObjectPreservesCallbackProperty(analysis, declarator.init, propertyName, isReactUseRefCall) ? propertyName : null;
42318
+ }
42319
+ if (!isNodeOfType(declarator.id, "Identifier")) return null;
42320
+ currentExpression = stripParenExpression(declarator.init);
42321
+ }
42322
+ if (!isNodeOfType(currentExpression, "MemberExpression")) return null;
42323
+ const propertyName = getStaticMemberPropertyName(currentExpression);
42324
+ if (!propertyName || !COMMAND_PROP_NAME_PATTERN.test(propertyName)) return null;
42325
+ return refCurrentObjectPreservesCallbackProperty(analysis, currentExpression.object, propertyName, isReactUseRefCall) ? propertyName : null;
42326
+ };
41231
42327
  const isWrapperHookCallbackRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
41232
42328
  const node = def.node;
41233
42329
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
@@ -41329,6 +42425,8 @@ const noPassDataToParent = defineRule({
41329
42425
  if (callbackRefProvenance) {
41330
42426
  if ([...callbackRefProvenance.callbackPropNames].some((callbackPropName) => COMMAND_PROP_NAME_PATTERN.test(callbackPropName))) continue;
41331
42427
  } else if (calleeNode === identifier) {
42428
+ const callbackPropName = getCommandCallbackPropName(analysis, identifier, isReactUseRefCall);
42429
+ if (callbackPropName && COMMAND_PROP_NAME_PATTERN.test(callbackPropName)) continue;
41332
42430
  if (!isDirectParentCallbackRef(analysis, ref)) continue;
41333
42431
  if (isNodeOfType(identifier, "Identifier") && COMMAND_PROP_NAME_PATTERN.test(identifier.name)) continue;
41334
42432
  } else if (isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === identifier) {
@@ -42520,7 +43618,16 @@ const getElementImplicitRoles = (tag) => IMPLICIT_ROLES_BY_TAG.get(tag) ?? EMPTY
42520
43618
  const getTagsForRole = (role) => TAGS_BY_ROLE.get(role) ?? EMPTY_ROLE_LIST;
42521
43619
  //#endregion
42522
43620
  //#region src/plugin/utils/get-implicit-role.ts
42523
- const getImplicitRole = (node, elementType) => {
43621
+ const getInputTypeImplicitRole = (inputTypeValue) => {
43622
+ const inputType = inputTypeValue.toLowerCase();
43623
+ if (inputType === "button" || inputType === "image" || inputType === "reset" || inputType === "submit") return "button";
43624
+ if (inputType === "checkbox") return "checkbox";
43625
+ if (inputType === "number") return "spinbutton";
43626
+ if (inputType === "radio") return "radio";
43627
+ if (inputType === "range") return "slider";
43628
+ return "textbox";
43629
+ };
43630
+ const getImplicitRole = (node, elementType, scopes) => {
42524
43631
  const propStringValue = (propName) => {
42525
43632
  const attribute = hasJsxPropIgnoreCase(node.attributes, propName);
42526
43633
  return attribute ? getJsxPropStringValue(attribute) : null;
@@ -42578,13 +43685,18 @@ const getImplicitRole = (node, elementType) => {
42578
43685
  break;
42579
43686
  }
42580
43687
  case "input": {
42581
- const inputType = propStringValue("type");
42582
- if (inputType === null) implicit = "textbox";
42583
- else if (inputType === "button" || inputType === "image" || inputType === "reset" || inputType === "submit") implicit = "button";
42584
- else if (inputType === "checkbox") implicit = "checkbox";
42585
- else if (inputType === "radio") implicit = "radio";
42586
- else if (inputType === "range") implicit = "slider";
42587
- else implicit = "textbox";
43688
+ const inputTypeAttribute = hasJsxPropIgnoreCase(node.attributes, "type");
43689
+ if (!inputTypeAttribute) {
43690
+ implicit = "textbox";
43691
+ break;
43692
+ }
43693
+ const inputTypeValues = getJsxPropStaticStringValues(inputTypeAttribute, scopes);
43694
+ if (inputTypeValues === null) {
43695
+ implicit = "";
43696
+ break;
43697
+ }
43698
+ const implicitRoles = new Set(inputTypeValues.map(getInputTypeImplicitRole));
43699
+ implicit = implicitRoles.size === 1 ? implicitRoles.values().next().value ?? "" : "";
42588
43700
  break;
42589
43701
  }
42590
43702
  case "li":
@@ -42688,7 +43800,7 @@ const noRedundantRoles = defineRule({
42688
43800
  if (tag === "td" || tag === "th") {
42689
43801
  if (findSameFileTableContext(node, context.settings) !== "table") return;
42690
43802
  implicitRoles = [getTableCellPrimaryRole(node, tag)];
42691
- } else if (ATTRIBUTE_DEPENDENT_IMPLICIT_ROLE_TAGS.has(tag)) implicitRoles = [getImplicitRole(node, tag)].filter((resolvedRole) => resolvedRole !== null);
43803
+ } else if (ATTRIBUTE_DEPENDENT_IMPLICIT_ROLE_TAGS.has(tag)) implicitRoles = [getImplicitRole(node, tag, context.scopes)].filter((resolvedRole) => resolvedRole !== null);
42692
43804
  else implicitRoles = getElementImplicitRoles(tag);
42693
43805
  const allowedHere = [...DEFAULT_NON_REDUNDANT_ROLES[tag] ?? [], ...settings.exceptions[tag] ?? []];
42694
43806
  if (implicitRoles.includes(role) && !allowedHere.includes(role)) context.report({
@@ -42867,9 +43979,9 @@ const isInsideComponentContext = (node) => {
42867
43979
  }
42868
43980
  return false;
42869
43981
  };
42870
- const functionBodyOf = (node) => {
42871
- if (isFunctionLike$1(node)) return node.body ?? null;
42872
- if (isNodeOfType(node, "VariableDeclarator") && node.init && isFunctionLike$1(node.init)) return node.init.body ?? null;
43982
+ const getFunctionFromDeclaration = (node) => {
43983
+ if (isFunctionLike$1(node)) return node;
43984
+ if (isNodeOfType(node, "VariableDeclarator") && node.init && isFunctionLike$1(node.init)) return node.init;
42873
43985
  return null;
42874
43986
  };
42875
43987
  const isHookCallee = (callee) => {
@@ -42877,23 +43989,43 @@ const isHookCallee = (callee) => {
42877
43989
  if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.object, "Identifier") && isUppercaseName(callee.object.name) && isNodeOfType(callee.property, "Identifier")) return isReactHookName(callee.property.name);
42878
43990
  return false;
42879
43991
  };
42880
- const containsHookCall = (body) => {
42881
- let found = false;
42882
- walkAst(body, (child) => {
42883
- if (found) return false;
42884
- if (child !== body && isFunctionLike$1(child) && isComponentFunction$1(child)) return false;
42885
- if (!isNodeOfType(child, "CallExpression")) return;
42886
- if (isHookCallee(child.callee)) found = true;
43992
+ const containsReachableHookCall = (functionNode, rootFunction, context, visitedFunctions) => {
43993
+ if (!isFunctionLike$1(functionNode) || visitedFunctions.has(functionNode)) return false;
43994
+ visitedFunctions.add(functionNode);
43995
+ let didFindReachableHook = false;
43996
+ walkAst(functionNode.body, (child) => {
43997
+ if (didFindReachableHook) return false;
43998
+ if (isFunctionLike$1(child) && !executesDuringRender(child, context.scopes)) return false;
43999
+ if (!isNodeOfType(child, "CallExpression") && !isNodeOfType(child, "NewExpression")) return;
44000
+ if (isNodeOfType(child, "CallExpression")) {
44001
+ if (isHookCallee(child.callee)) {
44002
+ didFindReachableHook = true;
44003
+ return false;
44004
+ }
44005
+ const calledFunction = resolveExactLocalFunction(child.callee, context.scopes);
44006
+ if (calledFunction && isAstDescendant(calledFunction, rootFunction) && containsReachableHookCall(calledFunction, rootFunction, context, visitedFunctions)) {
44007
+ didFindReachableHook = true;
44008
+ return false;
44009
+ }
44010
+ }
44011
+ for (const callArgument of child.arguments ?? []) {
44012
+ if (!executesDuringRender(callArgument, context.scopes)) continue;
44013
+ const callbackFunction = resolveExactLocalFunction(callArgument, context.scopes);
44014
+ if (callbackFunction && isAstDescendant(callbackFunction, rootFunction) && containsReachableHookCall(callbackFunction, rootFunction, context, visitedFunctions)) {
44015
+ didFindReachableHook = true;
44016
+ return false;
44017
+ }
44018
+ }
42887
44019
  });
42888
- return found;
44020
+ return didFindReachableHook;
42889
44021
  };
42890
- const isHookCallingRenderHelper = (symbol) => {
44022
+ const isHookCallingRenderHelper = (symbol, context) => {
42891
44023
  if (!symbol) return false;
42892
44024
  const declaration = symbol.declarationNode;
42893
44025
  if (!isNodeOfType(declaration, "FunctionDeclaration") && !isNodeOfType(declaration, "VariableDeclarator")) return false;
42894
- const body = functionBodyOf(declaration);
42895
- if (!body) return false;
42896
- return containsHookCall(body);
44026
+ const functionNode = getFunctionFromDeclaration(declaration);
44027
+ if (!functionNode) return false;
44028
+ return containsReachableHookCall(functionNode, functionNode, context, /* @__PURE__ */ new Set());
42897
44029
  };
42898
44030
  const noRenderInRender = defineRule({
42899
44031
  id: "no-render-in-render",
@@ -42908,7 +44040,7 @@ const noRenderInRender = defineRule({
42908
44040
  const calleeName = expression.callee.name;
42909
44041
  if (!RENDER_FUNCTION_PATTERN.test(calleeName)) return;
42910
44042
  if (!isInsideComponentContext(node)) return;
42911
- if (!isHookCallingRenderHelper(context.scopes.symbolFor(expression.callee))) return;
44043
+ if (!isHookCallingRenderHelper(context.scopes.symbolFor(expression.callee), context)) return;
42912
44044
  context.report({
42913
44045
  node: expression,
42914
44046
  message: `"${calleeName}()" hides a component behind an inline call, so pull it into its own component and render it as JSX so React can track it.`
@@ -42992,6 +44124,31 @@ const noRenderReturnValue = defineRule({
42992
44124
  });
42993
44125
  //#endregion
42994
44126
  //#region src/plugin/rules/state-and-effects/no-reset-all-state-on-prop-change.ts
44127
+ const SYNCHRONOUS_ARRAY_CALLBACK_METHOD_NAMES = new Set([
44128
+ "every",
44129
+ "filter",
44130
+ "find",
44131
+ "findIndex",
44132
+ "flatMap",
44133
+ "forEach",
44134
+ "map",
44135
+ "reduce",
44136
+ "reduceRight",
44137
+ "some"
44138
+ ]);
44139
+ const createConstantFormula = (constantValue) => ({
44140
+ kind: "constant",
44141
+ constantValue
44142
+ });
44143
+ const createNotFormula = (formula) => ({
44144
+ kind: "not",
44145
+ left: formula
44146
+ });
44147
+ const createBinaryFormula = (kind, left, right) => ({
44148
+ kind,
44149
+ left,
44150
+ right
44151
+ });
42995
44152
  const isUndefinedNode = (node) => {
42996
44153
  if (node === null || node === void 0) return true;
42997
44154
  return isNodeOfType(node, "Identifier") && node.name === "undefined";
@@ -43053,6 +44210,241 @@ const getLivePropExpressionIdentity = (analysis, context, node, visitedSymbolIds
43053
44210
  }
43054
44211
  return null;
43055
44212
  };
44213
+ const getLivePropAtomKey = (identity) => `prop:${identity.propSymbolId}:${JSON.stringify(identity.memberPath)}:${identity.booleanNormalization}`;
44214
+ const getBooleanFormula$1 = (analysis, context, node, protectedSymbolIds, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
44215
+ const expression = stripParenExpression(node);
44216
+ if (isNodeOfType(expression, "Literal")) return createConstantFormula(Boolean(expression.value));
44217
+ if (isNodeOfType(expression, "Identifier")) {
44218
+ if (expression.name === "undefined" && context.scopes.isGlobalReference(expression)) return createConstantFormula(false);
44219
+ const symbol = context.scopes.symbolFor(expression);
44220
+ if (!symbol) return null;
44221
+ if (!protectedSymbolIds.has(symbol.id) && symbol.kind === "const" && !visitedSymbolIds.has(symbol.id) && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && symbol.declarationNode.id === symbol.bindingIdentifier) {
44222
+ const initializer = getDirectConstInitializer(symbol);
44223
+ if (initializer) {
44224
+ visitedSymbolIds.add(symbol.id);
44225
+ const initializerFormula = getBooleanFormula$1(analysis, context, initializer, protectedSymbolIds, visitedSymbolIds);
44226
+ if (initializerFormula) return initializerFormula;
44227
+ }
44228
+ }
44229
+ return {
44230
+ kind: "atom",
44231
+ atomKey: `symbol:${symbol.id}`
44232
+ };
44233
+ }
44234
+ if (isNodeOfType(expression, "MemberExpression")) {
44235
+ const identity = getLivePropExpressionIdentity(analysis, context, expression);
44236
+ return identity ? {
44237
+ kind: "atom",
44238
+ atomKey: getLivePropAtomKey(identity)
44239
+ } : null;
44240
+ }
44241
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
44242
+ const argumentFormula = getBooleanFormula$1(analysis, context, expression.argument, protectedSymbolIds, visitedSymbolIds);
44243
+ return argumentFormula ? createNotFormula(argumentFormula) : null;
44244
+ }
44245
+ if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee) && expression.arguments.length === 1 && !isNodeOfType(expression.arguments[0], "SpreadElement")) return getBooleanFormula$1(analysis, context, expression.arguments[0], protectedSymbolIds, visitedSymbolIds);
44246
+ if (isNodeOfType(expression, "LogicalExpression") && (expression.operator === "&&" || expression.operator === "||")) {
44247
+ const leftFormula = getBooleanFormula$1(analysis, context, expression.left, protectedSymbolIds, new Set(visitedSymbolIds));
44248
+ const rightFormula = getBooleanFormula$1(analysis, context, expression.right, protectedSymbolIds, new Set(visitedSymbolIds));
44249
+ if (!leftFormula || !rightFormula) return null;
44250
+ return createBinaryFormula(expression.operator === "&&" ? "and" : "or", leftFormula, rightFormula);
44251
+ }
44252
+ if (isNodeOfType(expression, "ConditionalExpression")) {
44253
+ const testFormula = getBooleanFormula$1(analysis, context, expression.test, protectedSymbolIds, new Set(visitedSymbolIds));
44254
+ const consequentFormula = getBooleanFormula$1(analysis, context, expression.consequent, protectedSymbolIds, new Set(visitedSymbolIds));
44255
+ const alternateFormula = getBooleanFormula$1(analysis, context, expression.alternate, protectedSymbolIds, new Set(visitedSymbolIds));
44256
+ if (!testFormula || !consequentFormula || !alternateFormula) return null;
44257
+ return createBinaryFormula("or", createBinaryFormula("and", testFormula, consequentFormula), createBinaryFormula("and", createNotFormula(testFormula), alternateFormula));
44258
+ }
44259
+ if (isNodeOfType(expression, "BinaryExpression") && (expression.operator === "===" || expression.operator === "==" || expression.operator === "!==" || expression.operator === "!=")) {
44260
+ const leftIsBoolean = isNodeOfType(expression.left, "Literal") && typeof expression.left.value === "boolean";
44261
+ if (leftIsBoolean === (isNodeOfType(expression.right, "Literal") && typeof expression.right.value === "boolean")) return null;
44262
+ const booleanLiteral = leftIsBoolean ? expression.left : expression.right;
44263
+ const comparedFormula = getBooleanFormula$1(analysis, context, leftIsBoolean ? expression.right : expression.left, protectedSymbolIds, visitedSymbolIds);
44264
+ if (!comparedFormula || !isNodeOfType(booleanLiteral, "Literal")) return null;
44265
+ return (expression.operator === "===" || expression.operator === "==" ? booleanLiteral.value : !booleanLiteral.value) ? comparedFormula : createNotFormula(comparedFormula);
44266
+ }
44267
+ return null;
44268
+ };
44269
+ const getRequiredTruthyConditions = (analysis, context, node, protectedSymbolIds) => {
44270
+ const formula = getBooleanFormula$1(analysis, context, node, protectedSymbolIds);
44271
+ if (formula) return [formula];
44272
+ const expression = stripParenExpression(node);
44273
+ if (!isNodeOfType(expression, "LogicalExpression") || expression.operator !== "&&") return [];
44274
+ return [...getRequiredTruthyConditions(analysis, context, expression.left, protectedSymbolIds), ...getRequiredTruthyConditions(analysis, context, expression.right, protectedSymbolIds)];
44275
+ };
44276
+ const evaluateBooleanFormula$1 = (formula, assignments) => {
44277
+ if (formula.kind === "constant") return formula.constantValue ?? null;
44278
+ if (formula.kind === "atom") return formula.atomKey === void 0 ? null : assignments.get(formula.atomKey) ?? null;
44279
+ if (formula.kind === "not") {
44280
+ if (!formula.left) return null;
44281
+ const value = evaluateBooleanFormula$1(formula.left, assignments);
44282
+ return value === null ? null : !value;
44283
+ }
44284
+ if (!formula.left || !formula.right) return null;
44285
+ const leftValue = evaluateBooleanFormula$1(formula.left, assignments);
44286
+ const rightValue = evaluateBooleanFormula$1(formula.right, assignments);
44287
+ if (formula.kind === "and") {
44288
+ if (leftValue === false || rightValue === false) return false;
44289
+ return leftValue === true && rightValue === true ? true : null;
44290
+ }
44291
+ if (leftValue === true || rightValue === true) return true;
44292
+ return leftValue === false && rightValue === false ? false : null;
44293
+ };
44294
+ const assignBooleanFact = (facts, atomKey, value) => {
44295
+ const existingValue = facts.assignments.get(atomKey);
44296
+ if (existingValue === void 0) {
44297
+ facts.assignments.set(atomKey, value);
44298
+ facts.didChange = true;
44299
+ } else if (existingValue !== value) facts.didConflict = true;
44300
+ };
44301
+ const addRequiredBooleanFacts = (formula, expectedValue, facts) => {
44302
+ const existingValue = evaluateBooleanFormula$1(formula, facts.assignments);
44303
+ if (existingValue !== null) {
44304
+ if (existingValue !== expectedValue) facts.didConflict = true;
44305
+ return;
44306
+ }
44307
+ if (formula.kind === "atom" && formula.atomKey !== void 0) {
44308
+ assignBooleanFact(facts, formula.atomKey, expectedValue);
44309
+ return;
44310
+ }
44311
+ if (formula.kind === "not" && formula.left) {
44312
+ addRequiredBooleanFacts(formula.left, !expectedValue, facts);
44313
+ return;
44314
+ }
44315
+ if (!formula.left || !formula.right) return;
44316
+ if (formula.kind === "and") {
44317
+ if (expectedValue) {
44318
+ addRequiredBooleanFacts(formula.left, true, facts);
44319
+ addRequiredBooleanFacts(formula.right, true, facts);
44320
+ return;
44321
+ }
44322
+ const leftValue = evaluateBooleanFormula$1(formula.left, facts.assignments);
44323
+ const rightValue = evaluateBooleanFormula$1(formula.right, facts.assignments);
44324
+ if (leftValue === true) addRequiredBooleanFacts(formula.right, false, facts);
44325
+ if (rightValue === true) addRequiredBooleanFacts(formula.left, false, facts);
44326
+ return;
44327
+ }
44328
+ if (!expectedValue) {
44329
+ addRequiredBooleanFacts(formula.left, false, facts);
44330
+ addRequiredBooleanFacts(formula.right, false, facts);
44331
+ return;
44332
+ }
44333
+ const leftValue = evaluateBooleanFormula$1(formula.left, facts.assignments);
44334
+ const rightValue = evaluateBooleanFormula$1(formula.right, facts.assignments);
44335
+ if (leftValue === false) addRequiredBooleanFacts(formula.right, true, facts);
44336
+ if (rightValue === false) addRequiredBooleanFacts(formula.left, true, facts);
44337
+ };
44338
+ const doConditionsImplyFormula = (conditions, target) => {
44339
+ const facts = {
44340
+ assignments: /* @__PURE__ */ new Map(),
44341
+ didConflict: false,
44342
+ didChange: true
44343
+ };
44344
+ while (facts.didChange && !facts.didConflict) {
44345
+ facts.didChange = false;
44346
+ for (const condition of conditions) addRequiredBooleanFacts(condition, true, facts);
44347
+ }
44348
+ return facts.didConflict || evaluateBooleanFormula$1(target, facts.assignments) === true;
44349
+ };
44350
+ const getFunctionBindingSymbol = (functionNode, scopes) => {
44351
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) return scopes.symbolFor(functionNode.id);
44352
+ const parent = functionNode.parent;
44353
+ if ((isNodeOfType(functionNode, "ArrowFunctionExpression") || isNodeOfType(functionNode, "FunctionExpression")) && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return scopes.symbolFor(parent.id);
44354
+ return null;
44355
+ };
44356
+ const getComponentFunctionNode = (containingNode) => {
44357
+ if (isFunctionLike$1(containingNode)) return containingNode;
44358
+ if (!isNodeOfType(containingNode, "VariableDeclarator") || !containingNode.init) return null;
44359
+ const initializer = stripParenExpression(containingNode.init);
44360
+ if (isFunctionLike$1(initializer)) return initializer;
44361
+ if (!isNodeOfType(initializer, "CallExpression")) return null;
44362
+ const firstArgument = initializer.arguments[0];
44363
+ return firstArgument && !isNodeOfType(firstArgument, "SpreadElement") && isFunctionLike$1(firstArgument) ? firstArgument : null;
44364
+ };
44365
+ const isReferenceDirectlyCalled = (identifier) => {
44366
+ const unwrappedIdentifier = stripParenExpression(identifier);
44367
+ const parent = unwrappedIdentifier.parent;
44368
+ return isNodeOfType(parent, "CallExpression") && parent.callee === unwrappedIdentifier ? parent : null;
44369
+ };
44370
+ const getSynchronousCallbackCall = (functionNode) => {
44371
+ const callExpression = functionNode.parent;
44372
+ if (!isNodeOfType(callExpression, "CallExpression") || !callExpression.arguments.some((argument) => argument === functionNode) || !isNodeOfType(callExpression.callee, "MemberExpression")) return null;
44373
+ const methodName = getStaticMemberPropertyName(callExpression.callee);
44374
+ return methodName && SYNCHRONOUS_ARRAY_CALLBACK_METHOD_NAMES.has(methodName) ? callExpression : null;
44375
+ };
44376
+ const isNodeEvaluatedDuringRender = (node, componentNode, scopes, visitedFunctionSymbolIds = /* @__PURE__ */ new Set()) => {
44377
+ const functionNode = findEnclosingFunction$1(node);
44378
+ if (!functionNode) return false;
44379
+ if (functionNode === componentNode) return true;
44380
+ const synchronousCallbackCall = getSynchronousCallbackCall(functionNode);
44381
+ if (synchronousCallbackCall) return isNodeEvaluatedDuringRender(synchronousCallbackCall, componentNode, scopes, visitedFunctionSymbolIds);
44382
+ if (executesDuringRender(functionNode, scopes)) return isNodeEvaluatedDuringRender(functionNode.parent ?? functionNode, componentNode, scopes, visitedFunctionSymbolIds);
44383
+ const functionSymbol = getFunctionBindingSymbol(functionNode, scopes);
44384
+ if (!functionSymbol || visitedFunctionSymbolIds.has(functionSymbol.id)) return false;
44385
+ visitedFunctionSymbolIds.add(functionSymbol.id);
44386
+ let callCount = 0;
44387
+ for (const reference of functionSymbol.references) {
44388
+ const callExpression = isReferenceDirectlyCalled(reference.identifier);
44389
+ if (!callExpression) return false;
44390
+ if (!isNodeEvaluatedDuringRender(callExpression, componentNode, scopes, new Set(visitedFunctionSymbolIds))) return false;
44391
+ callCount += 1;
44392
+ }
44393
+ return callCount > 0;
44394
+ };
44395
+ const isInlineJsxCallback = (functionNode) => {
44396
+ let ancestor = functionNode.parent;
44397
+ while (ancestor) {
44398
+ if (isNodeOfType(ancestor, "JSXAttribute")) return true;
44399
+ if (isFunctionLike$1(ancestor) || isNodeOfType(ancestor, "Program")) return false;
44400
+ ancestor = ancestor.parent;
44401
+ }
44402
+ return false;
44403
+ };
44404
+ const collectExposureConditions = (analysis, context, node, componentNode, protectedSymbolIds) => {
44405
+ const conditions = [];
44406
+ let child = node;
44407
+ let parent = node.parent;
44408
+ while (parent) {
44409
+ if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) {
44410
+ if (parent.operator === "&&") conditions.push(...getRequiredTruthyConditions(analysis, context, parent.left, protectedSymbolIds));
44411
+ if (parent.operator === "||") {
44412
+ const leftFormula = getBooleanFormula$1(analysis, context, parent.left, protectedSymbolIds);
44413
+ if (leftFormula) conditions.push(createNotFormula(leftFormula));
44414
+ }
44415
+ } else if (isNodeOfType(parent, "ConditionalExpression")) {
44416
+ const testFormula = getBooleanFormula$1(analysis, context, parent.test, protectedSymbolIds);
44417
+ if (testFormula && parent.consequent === child) conditions.push(testFormula);
44418
+ if (testFormula && parent.alternate === child) conditions.push(createNotFormula(testFormula));
44419
+ } else if (isNodeOfType(parent, "IfStatement")) {
44420
+ const testFormula = getBooleanFormula$1(analysis, context, parent.test, protectedSymbolIds);
44421
+ if (testFormula && parent.consequent === child) conditions.push(testFormula);
44422
+ if (testFormula && parent.alternate === child) conditions.push(createNotFormula(testFormula));
44423
+ }
44424
+ if (parent === componentNode) break;
44425
+ if (isFunctionLike$1(parent) && parent !== componentNode && !isInlineJsxCallback(parent)) {
44426
+ const synchronousCallbackCall = getSynchronousCallbackCall(parent);
44427
+ if (synchronousCallbackCall) {
44428
+ child = synchronousCallbackCall;
44429
+ parent = synchronousCallbackCall.parent;
44430
+ continue;
44431
+ }
44432
+ const functionSymbol = getFunctionBindingSymbol(parent, context.scopes);
44433
+ if (functionSymbol?.references.length === 1) {
44434
+ const callExpression = isReferenceDirectlyCalled(functionSymbol.references[0].identifier);
44435
+ if (callExpression) {
44436
+ child = callExpression;
44437
+ parent = callExpression.parent;
44438
+ continue;
44439
+ }
44440
+ }
44441
+ break;
44442
+ }
44443
+ child = parent;
44444
+ parent = parent.parent;
44445
+ }
44446
+ return conditions;
44447
+ };
43056
44448
  const isMountSnapshotInitializer = (context, node) => {
43057
44449
  if (!isReactApiCall(node, "useMemo", context.scopes, { resolveNamedAliases: true }) || !isNodeOfType(node, "CallExpression")) return false;
43058
44450
  const dependencies = node.arguments?.[1];
@@ -43128,13 +44520,194 @@ const countUseStates = (analysis, componentNode) => {
43128
44520
  for (const ref of getDownstreamRefs(analysis, componentNode)) if (isState(analysis, ref)) stateVariables.add(ref.resolved);
43129
44521
  return stateVariables.size;
43130
44522
  };
44523
+ const hasUnconditionalAwaitBefore = (functionNode, boundaryNode, context) => {
44524
+ const boundaryStart = getRangeStart(boundaryNode);
44525
+ if (boundaryStart === null) return false;
44526
+ let didFindAwait = false;
44527
+ walkAst(functionNode, (child) => {
44528
+ if (didFindAwait) return false;
44529
+ if (child !== functionNode && isFunctionLike$1(child)) return false;
44530
+ if (!isNodeOfType(child, "AwaitExpression") || !isNodeReachableWithinFunction(child, context) || !context.cfg.isUnconditionalFromEntry(child)) return;
44531
+ const awaitStart = getRangeStart(child);
44532
+ if (awaitStart !== null && awaitStart < boundaryStart) {
44533
+ didFindAwait = true;
44534
+ return false;
44535
+ }
44536
+ });
44537
+ return didFindAwait;
44538
+ };
44539
+ const helperReloadsSetterAfterAwait = (analysis, context, helperFunction, setterReference) => getDownstreamRefs(analysis, helperFunction).some((helperReference) => {
44540
+ if (!setterReference.resolved || helperReference.resolved !== setterReference.resolved) return false;
44541
+ const helperSetterCall = getCallExpr(helperReference);
44542
+ return Boolean(helperSetterCall && findEnclosingFunction$1(helperSetterCall) === helperFunction && isNodeReachableWithinFunction(helperSetterCall, context) && hasUnconditionalAwaitBefore(helperFunction, helperSetterCall, context));
44543
+ });
44544
+ const effectInvokesAsyncSetterReloadHelper = (analysis, context, effectReferences, effectFunction, setterReference) => effectReferences.some((effectReference) => {
44545
+ const helperCall = getCallExpr(effectReference);
44546
+ if (!isNodeOfType(helperCall, "CallExpression") || findEnclosingFunction$1(helperCall) !== effectFunction || !isNodeReachableWithinFunction(helperCall, context)) return false;
44547
+ const helperFunction = resolveExactLocalFunction(helperCall.callee, context.scopes);
44548
+ return Boolean(helperFunction && helperFunction !== effectFunction && helperReloadsSetterAfterAwait(analysis, context, helperFunction, setterReference));
44549
+ });
44550
+ const getStateSymbolForSetter = (analysis, context, setterReference) => {
44551
+ const useStateDeclaration = getUseStateDecl(analysis, setterReference);
44552
+ if (!useStateDeclaration || !isNodeOfType(useStateDeclaration, "VariableDeclarator") || !isNodeOfType(useStateDeclaration.id, "ArrayPattern")) return null;
44553
+ const stateBinding = useStateDeclaration.id.elements[0];
44554
+ return stateBinding && isNodeOfType(stateBinding, "Identifier") ? context.scopes.symbolFor(stateBinding) : null;
44555
+ };
44556
+ const getPropertyName = (node) => {
44557
+ if (isNodeOfType(node, "Identifier")) return node.name;
44558
+ return isNodeOfType(node, "Literal") && typeof node.value === "string" ? node.value : null;
44559
+ };
44560
+ const isBooleanTypeNode = (node) => {
44561
+ if (!node) return false;
44562
+ if (isNodeOfType(node, "TSBooleanKeyword")) return true;
44563
+ if (!isNodeOfType(node, "TSUnionType")) return false;
44564
+ return node.types.every((typeNode) => isNodeOfType(typeNode, "TSBooleanKeyword") || isNodeOfType(typeNode, "TSUndefinedKeyword") || isNodeOfType(typeNode, "TSNullKeyword"));
44565
+ };
44566
+ const getBooleanPropertyType = (typeNode, propertyName, referenceNode) => {
44567
+ const unwrappedType = isNodeOfType(typeNode, "TSTypeAnnotation") ? typeNode.typeAnnotation : typeNode;
44568
+ if (isNodeOfType(unwrappedType, "TSTypeLiteral")) return unwrappedType.members.some((member) => isNodeOfType(member, "TSPropertySignature") && getPropertyName(member.key) === propertyName && isBooleanTypeNode(member.typeAnnotation?.typeAnnotation));
44569
+ if (!isNodeOfType(unwrappedType, "TSTypeReference") || !isNodeOfType(unwrappedType.typeName, "Identifier")) return false;
44570
+ const typeName = unwrappedType.typeName.name;
44571
+ if (hasEnclosingTypeParameterNamed(referenceNode, typeName)) return false;
44572
+ const programNode = findProgramNode(referenceNode);
44573
+ if (!isNodeOfType(programNode, "Program")) return false;
44574
+ const matchingInterfaces = programNode.body.flatMap((statement) => {
44575
+ const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
44576
+ return isNodeOfType(declaration, "TSInterfaceDeclaration") && declaration.id.name === typeName ? [declaration] : [];
44577
+ });
44578
+ if (matchingInterfaces.length !== 1) return false;
44579
+ let sameNameTypeBindingCount = 0;
44580
+ walkAst(programNode, (candidate) => {
44581
+ const identifier = isNodeOfType(candidate, "TSInterfaceDeclaration") || isNodeOfType(candidate, "TSTypeAliasDeclaration") || isNodeOfType(candidate, "ClassDeclaration") || isNodeOfType(candidate, "ClassExpression") || isNodeOfType(candidate, "TSEnumDeclaration") ? candidate.id : isNodeOfType(candidate, "TSTypeParameter") ? candidate.name : null;
44582
+ if (isNodeOfType(identifier, "Identifier") && identifier.name === typeName) sameNameTypeBindingCount += 1;
44583
+ });
44584
+ if (sameNameTypeBindingCount !== 1) return false;
44585
+ return matchingInterfaces[0].body.body.some((member) => isNodeOfType(member, "TSPropertySignature") && getPropertyName(member.key) === propertyName && isBooleanTypeNode(member.typeAnnotation?.typeAnnotation));
44586
+ };
44587
+ const findProgramNode = (node) => {
44588
+ let currentNode = node;
44589
+ while (currentNode.parent) currentNode = currentNode.parent;
44590
+ return currentNode;
44591
+ };
44592
+ const hasBooleanBindingAnnotation = (symbol, identifier) => {
44593
+ const property = symbol.bindingIdentifier.parent;
44594
+ const objectPattern = property?.parent;
44595
+ if (!isNodeOfType(property, "Property") || !isNodeOfType(objectPattern, "ObjectPattern") || !objectPattern.typeAnnotation) return false;
44596
+ const propertyName = getPropertyName(property.key);
44597
+ return Boolean(propertyName && getBooleanPropertyType(objectPattern.typeAnnotation, propertyName, identifier));
44598
+ };
44599
+ const isBooleanExpression$1 = (context, node, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
44600
+ const expression = stripParenExpression(node);
44601
+ if (isNodeOfType(expression, "Literal")) return typeof expression.value === "boolean";
44602
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return true;
44603
+ if (isNodeOfType(expression, "BinaryExpression")) return [
44604
+ "==",
44605
+ "===",
44606
+ "!=",
44607
+ "!==",
44608
+ "<",
44609
+ "<=",
44610
+ ">",
44611
+ ">="
44612
+ ].includes(expression.operator);
44613
+ if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee)) return true;
44614
+ if (isNodeOfType(expression, "LogicalExpression")) return isBooleanExpression$1(context, expression.left, new Set(visitedSymbolIds)) && isBooleanExpression$1(context, expression.right, new Set(visitedSymbolIds));
44615
+ if (isNodeOfType(expression, "ConditionalExpression")) return isBooleanExpression$1(context, expression.consequent, new Set(visitedSymbolIds)) && isBooleanExpression$1(context, expression.alternate, new Set(visitedSymbolIds));
44616
+ if (!isNodeOfType(expression, "Identifier")) return false;
44617
+ const symbol = context.scopes.symbolFor(expression);
44618
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
44619
+ if (hasBooleanBindingAnnotation(symbol, expression)) return true;
44620
+ visitedSymbolIds.add(symbol.id);
44621
+ const initializer = getDirectConstInitializer(symbol);
44622
+ return Boolean(initializer && isBooleanExpression$1(context, initializer, visitedSymbolIds));
44623
+ };
44624
+ const getPropDerivedDependencySymbols = (analysis, context, dependencyReferences) => {
44625
+ const symbolsById = /* @__PURE__ */ new Map();
44626
+ for (const dependencyReference of dependencyReferences) {
44627
+ if (!getUpstreamRefs(analysis, dependencyReference).some((upstreamReference) => isProp(analysis, upstreamReference))) continue;
44628
+ const symbol = context.scopes.symbolFor(dependencyReference.identifier);
44629
+ if (symbol) symbolsById.set(symbol.id, symbol);
44630
+ }
44631
+ return [...symbolsById.values()];
44632
+ };
44633
+ const getImpliedDependencyValue = (conditions, dependencyFormulas) => {
44634
+ const impliesTrue = dependencyFormulas.some((dependencyFormula) => doConditionsImplyFormula(conditions, dependencyFormula));
44635
+ if (impliesTrue === dependencyFormulas.some((dependencyFormula) => doConditionsImplyFormula(conditions, createNotFormula(dependencyFormula)))) return null;
44636
+ return impliesTrue;
44637
+ };
44638
+ const getSetterExposureConditions = (analysis, context, setterReference, componentNode, protectedSymbolIds) => {
44639
+ const functionNode = findEnclosingFunction$1(setterReference.identifier);
44640
+ if (!functionNode) return null;
44641
+ if (isInlineJsxCallback(functionNode)) return [collectExposureConditions(analysis, context, functionNode, componentNode, protectedSymbolIds)];
44642
+ const functionSymbol = getFunctionBindingSymbol(functionNode, context.scopes);
44643
+ if (!functionSymbol || functionSymbol.references.length === 0) return null;
44644
+ const conditionsByReference = [];
44645
+ for (const reference of functionSymbol.references) {
44646
+ if (isReferenceDirectlyCalled(reference.identifier)) return null;
44647
+ let ancestor = reference.identifier.parent;
44648
+ while (ancestor && !isNodeOfType(ancestor, "JSXAttribute") && !isFunctionLike$1(ancestor)) ancestor = ancestor.parent;
44649
+ if (!isNodeOfType(ancestor, "JSXAttribute")) return null;
44650
+ conditionsByReference.push(collectExposureConditions(analysis, context, reference.identifier, componentNode, protectedSymbolIds));
44651
+ }
44652
+ return conditionsByReference;
44653
+ };
44654
+ const areAllSetterWritesVisibilityGuarded = (analysis, context, componentNode, resetSetterReferences, dependencySymbolIds, dependencyFormulas, visibleDependencyValue) => {
44655
+ const resetIdentifiers = new Set(resetSetterReferences.map((setterReference) => setterReference.identifier));
44656
+ const setterVariables = new Set(resetSetterReferences.map((reference) => reference.resolved));
44657
+ for (const setterVariable of setterVariables) {
44658
+ if (!setterVariable) return false;
44659
+ for (const setterReference of setterVariable.references) {
44660
+ if (setterVariable.identifiers.some((identifier) => identifier === setterReference.identifier)) continue;
44661
+ if (resetIdentifiers.has(setterReference.identifier)) continue;
44662
+ if (!getCallExpr(setterReference)) return false;
44663
+ const conditionsByExposure = getSetterExposureConditions(analysis, context, setterReference, componentNode, dependencySymbolIds);
44664
+ if (!conditionsByExposure || conditionsByExposure.some((conditions) => getImpliedDependencyValue(conditions, dependencyFormulas) !== visibleDependencyValue)) return false;
44665
+ }
44666
+ }
44667
+ return true;
44668
+ };
44669
+ const areAllResetStateReadsHiddenUntilReset = (analysis, context, componentNode, setterReferences, dependencyReferences) => {
44670
+ if (dependencyReferences.length !== 1) return false;
44671
+ const dependencySymbols = getPropDerivedDependencySymbols(analysis, context, dependencyReferences);
44672
+ if (dependencySymbols.length === 0 || dependencySymbols.some((symbol) => !isBooleanExpression$1(context, symbol.bindingIdentifier) || symbol.references.some((reference) => reference.flag !== "read"))) return false;
44673
+ const dependencySymbolIds = new Set(dependencySymbols.map((symbol) => symbol.id));
44674
+ const dependencyFormulas = dependencySymbols.map((symbol) => ({
44675
+ kind: "atom",
44676
+ atomKey: `symbol:${symbol.id}`
44677
+ }));
44678
+ const resetStateSymbolsById = /* @__PURE__ */ new Map();
44679
+ for (const setterReference of setterReferences) {
44680
+ const stateSymbol = getStateSymbolForSetter(analysis, context, setterReference);
44681
+ if (!stateSymbol) return false;
44682
+ resetStateSymbolsById.set(stateSymbol.id, stateSymbol);
44683
+ }
44684
+ let visibleDependencyValue = null;
44685
+ for (const stateSymbol of resetStateSymbolsById.values()) {
44686
+ let exposedReadCount = 0;
44687
+ for (const reference of stateSymbol.references) {
44688
+ if (reference.flag === "write") continue;
44689
+ const isRenderRead = isNodeEvaluatedDuringRender(reference.identifier, componentNode, context.scopes);
44690
+ const functionNode = findEnclosingFunction$1(reference.identifier);
44691
+ if (!isRenderRead && functionNode && !isInlineJsxCallback(functionNode)) return false;
44692
+ const referenceDependencyValue = getImpliedDependencyValue(collectExposureConditions(analysis, context, reference.identifier, componentNode, dependencySymbolIds), dependencyFormulas);
44693
+ if (referenceDependencyValue === null) return false;
44694
+ if (visibleDependencyValue !== null && visibleDependencyValue !== referenceDependencyValue) return false;
44695
+ visibleDependencyValue = referenceDependencyValue;
44696
+ exposedReadCount += 1;
44697
+ }
44698
+ if (exposedReadCount === 0) return false;
44699
+ }
44700
+ return Boolean(resetStateSymbolsById.size > 0 && visibleDependencyValue !== null && areAllSetterWritesVisibilityGuarded(analysis, context, componentNode, setterReferences, dependencySymbolIds, dependencyFormulas, visibleDependencyValue));
44701
+ };
43131
44702
  const findPropUsedToResetAllState = (analysis, context, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
43132
44703
  const stateSetterRefs = effectFnRefs.filter((ref) => isSyncStateSetterCall(analysis, ref, effectFn));
43133
44704
  if (stateSetterRefs.length === 0) return null;
43134
44705
  if (!stateSetterRefs.every((ref) => isSetStateToInitialValue(analysis, context, ref))) return null;
43135
- if (stateSetterRefs.every((setterRef) => effectFnRefs.some((otherRef) => otherRef !== setterRef && otherRef.resolved === setterRef.resolved && Boolean(getCallExpr(otherRef)) && !isSyncStateSetterCall(analysis, otherRef, effectFn)))) return null;
44706
+ if (stateSetterRefs.every((setterRef) => effectFnRefs.some((otherRef) => otherRef !== setterRef && otherRef.resolved === setterRef.resolved && Boolean(getCallExpr(otherRef)) && !isSyncStateSetterCall(analysis, otherRef, effectFn)) || effectInvokesAsyncSetterReloadHelper(analysis, context, effectFnRefs, effectFn, setterRef))) return null;
43136
44707
  const containing = findContainingNode(analysis, useEffectNode);
43137
44708
  if (new Set(stateSetterRefs.map((setterRef) => setterRef.resolved)).size !== countUseStates(analysis, containing)) return null;
44709
+ const componentFunctionNode = containing ? getComponentFunctionNode(containing) : null;
44710
+ if (componentFunctionNode && areAllResetStateReadsHiddenUntilReset(analysis, context, componentFunctionNode, stateSetterRefs, depsRefs)) return null;
43138
44711
  for (const depRef of depsRefs) for (const upRef of getUpstreamRefs(analysis, depRef)) if (isProp(analysis, upRef)) return upRef;
43139
44712
  return null;
43140
44713
  };
@@ -44827,9 +46400,13 @@ const UNCONTROLLED_INPUT_TAGS = new Set([
44827
46400
  "select"
44828
46401
  ]);
44829
46402
  const VALUE_BYPASS_INPUT_TYPES = new Set([
44830
- "hidden",
46403
+ "button",
44831
46404
  "checkbox",
44832
- "radio"
46405
+ "hidden",
46406
+ "image",
46407
+ "radio",
46408
+ "reset",
46409
+ "submit"
44833
46410
  ]);
44834
46411
  const VALUE_PARTNER_ATTRIBUTES = [
44835
46412
  "onChange",
@@ -44838,12 +46415,6 @@ const VALUE_PARTNER_ATTRIBUTES = [
44838
46415
  "disabled"
44839
46416
  ];
44840
46417
  const isLiteralFalseAttributeValue = (attribute) => isNodeOfType(attribute.value, "JSXExpressionContainer") && isNodeOfType(attribute.value.expression, "Literal") && attribute.value.expression.value === false;
44841
- const getInputTypeLiteral = (attributes) => {
44842
- const typeAttribute = findJsxAttribute(attributes, "type");
44843
- if (!typeAttribute || !isNodeOfType(typeAttribute.value, "Literal")) return null;
44844
- const value = typeAttribute.value.value;
44845
- return typeof value === "string" ? value : null;
44846
- };
44847
46418
  const isUseStateUndefinedInitializer = (init) => {
44848
46419
  if (!init || !isNodeOfType(init, "CallExpression")) return false;
44849
46420
  if (!isHookCall$2(init, "useState")) return false;
@@ -44886,9 +46457,18 @@ const noUncontrolledInput = defineRule({
44886
46457
  if (hasJsxSpreadAttribute(attributes)) return;
44887
46458
  const valueAttribute = findJsxAttribute(attributes, "value");
44888
46459
  if (!valueAttribute) return;
46460
+ let inputTypeCandidates = null;
46461
+ let hasExplicitInputType = false;
46462
+ let doesTypeBypassMissingOnChange = false;
46463
+ let doesTypeUseCheckedControlledness = false;
46464
+ let couldTypeUseCheckedControlledness = false;
44889
46465
  if (tagName === "input") {
44890
- const inputType = getInputTypeLiteral(attributes);
44891
- if (inputType !== null && VALUE_BYPASS_INPUT_TYPES.has(inputType)) return;
46466
+ const typeAttribute = findJsxAttribute([...attributes].reverse(), "type");
46467
+ hasExplicitInputType = Boolean(typeAttribute);
46468
+ inputTypeCandidates = typeAttribute ? getJsxPropExhaustiveStaticStringValues(typeAttribute, context.scopes) : null;
46469
+ doesTypeBypassMissingOnChange = Boolean(inputTypeCandidates !== null && inputTypeCandidates.length > 0 && inputTypeCandidates.every((inputTypeCandidate) => VALUE_BYPASS_INPUT_TYPES.has(inputTypeCandidate.toLowerCase())));
46470
+ doesTypeUseCheckedControlledness = Boolean(inputTypeCandidates !== null && inputTypeCandidates.length > 0 && inputTypeCandidates.every((inputTypeCandidate) => inputTypeCandidate === "checkbox" || inputTypeCandidate === "radio"));
46471
+ couldTypeUseCheckedControlledness = Boolean(hasExplicitInputType && (inputTypeCandidates === null || inputTypeCandidates.some((inputTypeCandidate) => inputTypeCandidate === "checkbox" || inputTypeCandidate === "radio")));
44892
46472
  }
44893
46473
  const hasAllowedPartner = VALUE_PARTNER_ATTRIBUTES.some((partnerAttributeName) => {
44894
46474
  const partnerAttribute = findJsxAttribute(attributes, partnerAttributeName);
@@ -44896,12 +46476,12 @@ const noUncontrolledInput = defineRule({
44896
46476
  if (partnerAttributeName === "disabled" && isLiteralFalseAttributeValue(partnerAttribute)) return false;
44897
46477
  return true;
44898
46478
  });
44899
- if (isNodeOfType(valueAttribute.value, "JSXExpressionContainer") && isNodeOfType(valueAttribute.value.expression, "Identifier") && undefinedInitialStateNames.has(valueAttribute.value.expression.name)) {
46479
+ if (isNodeOfType(valueAttribute.value, "JSXExpressionContainer") && isNodeOfType(valueAttribute.value.expression, "Identifier") && undefinedInitialStateNames.has(valueAttribute.value.expression.name) && !doesTypeUseCheckedControlledness) {
44900
46480
  const stateName = valueAttribute.value.expression.name;
44901
- const partnerHint = hasAllowedPartner ? "Give useState a starting value" : "Give useState a starting value and add onChange (or readOnly)";
46481
+ const partnerHint = hasAllowedPartner || doesTypeBypassMissingOnChange || couldTypeUseCheckedControlledness ? "Give useState a starting value" : "Give useState a starting value and add onChange (or readOnly)";
44902
46482
  context.report({
44903
46483
  node: child,
44904
- message: `This can trigger a console warning and reset the field because "${stateName}" starts undefined, so <${tagName} value={${stateName}}> flips from uncontrolled to controlled. ${partnerHint} (e.g. \`useState("")\`).`
46484
+ message: couldTypeUseCheckedControlledness ? `When \`type\` resolves to a value-controlled input type, this can trigger a console warning and reset the field because "${stateName}" starts undefined, so <input value={${stateName}}> can flip from uncontrolled to controlled. ${partnerHint} (e.g. \`useState("")\`).` : `This can trigger a console warning and reset the field because "${stateName}" starts undefined, so <${tagName} value={${stateName}}> flips from uncontrolled to controlled. ${partnerHint} (e.g. \`useState("")\`).`
44905
46485
  });
44906
46486
  return;
44907
46487
  }
@@ -44912,10 +46492,13 @@ const noUncontrolledInput = defineRule({
44912
46492
  });
44913
46493
  return;
44914
46494
  }
44915
- if (!hasAllowedPartner) context.report({
44916
- node: child,
44917
- message: `Your users can't type in this <${tagName} value={...}> because it has no \`onChange\` or \`readOnly\`, so add \`onChange\` (or \`readOnly\` if that's intended).`
44918
- });
46495
+ if (!hasAllowedPartner && !doesTypeBypassMissingOnChange) {
46496
+ const couldResolveToReadOnlyValueType = tagName === "input" && hasExplicitInputType && (inputTypeCandidates === null || inputTypeCandidates.some((inputTypeCandidate) => VALUE_BYPASS_INPUT_TYPES.has(inputTypeCandidate.toLowerCase())));
46497
+ context.report({
46498
+ node: child,
46499
+ message: couldResolveToReadOnlyValueType ? `When \`type\` resolves to an editable input type, users can't type in this <input value={...}> because it has no \`onChange\` or \`readOnly\`. Add \`onChange\` or \`readOnly\` unless \`type\` is always a read-only-value input type.` : `Your users can't type in this <${tagName} value={...}> because it has no \`onChange\` or \`readOnly\`, so add \`onChange\` (or \`readOnly\` if that's intended).`
46500
+ });
46501
+ }
44919
46502
  });
44920
46503
  };
44921
46504
  return {
@@ -48268,7 +49851,8 @@ const onlyExportComponents = defineRule({
48268
49851
  const declaration = child.declaration;
48269
49852
  if (isNodeOfType(declaration, "FunctionDeclaration") && declaration.id) {
48270
49853
  isExportedNodeIds.add(declaration);
48271
- exports.push(functionHasReactRenderSemantics(declaration, state) || localComponentNames.has(declaration.id.name) ? classifyExport(declaration.id.name, declaration.id, true, null, state) : {
49854
+ const classifiedExport = classifyExport(declaration.id.name, declaration.id, true, null, state);
49855
+ exports.push(functionHasReactRenderSemantics(declaration, state) || localComponentNames.has(declaration.id.name) || classifiedExport.kind === "allowed" ? classifiedExport : {
48272
49856
  kind: "non-component",
48273
49857
  reportNode: declaration.id
48274
49858
  });
@@ -51228,14 +52812,6 @@ const collectPluginEntries = (rawNode, scopes, visitedSymbolIds) => {
51228
52812
  }
51229
52813
  return entries;
51230
52814
  };
51231
- const findEffectiveExplicitAttribute = (attributes, attributeName) => {
51232
- for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
51233
- const attribute = attributes[attributeIndex];
51234
- if (!attribute || isNodeOfType(attribute, "JSXSpreadAttribute")) return null;
51235
- if (isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && attribute.name.name === attributeName) return attribute;
51236
- }
51237
- return null;
51238
- };
51239
52815
  const getAttributeExpression = (attribute) => {
51240
52816
  if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
51241
52817
  return isNodeOfType(attribute.value.expression, "JSXEmptyExpression") ? null : attribute.value.expression;
@@ -51275,7 +52851,7 @@ const hasDynamicUnsanitizedChildren = (openingElement, scopes) => {
51275
52851
  if (isNodeOfType(child.expression, "JSXEmptyExpression")) return false;
51276
52852
  return !isStaticOrSanitizedMarkdownExpression(child.expression, scopes, /* @__PURE__ */ new Set());
51277
52853
  });
51278
- const childrenAttribute = findEffectiveExplicitAttribute(openingElement.attributes, "children");
52854
+ const childrenAttribute = getAuthoritativeJsxAttribute(openingElement.attributes, "children");
51279
52855
  if (!childrenAttribute) return false;
51280
52856
  const childrenExpression = getAttributeExpression(childrenAttribute);
51281
52857
  return Boolean(childrenExpression && !isStaticOrSanitizedMarkdownExpression(childrenExpression, scopes, /* @__PURE__ */ new Set()));
@@ -51287,7 +52863,7 @@ const reactMarkdownUnsanitizedRawHtml = defineRule({
51287
52863
  recommendation: "Add `rehype-sanitize` to `rehypePlugins` or sanitize dynamic markdown before rendering. `skipHtml` does not disable HTML already parsed by `rehype-raw`.",
51288
52864
  create: skipNonProductionFiles((context) => ({ JSXOpeningElement(node) {
51289
52865
  if (!isReactMarkdownComponent(node.name, context.scopes)) return;
51290
- const pluginsAttribute = findEffectiveExplicitAttribute(node.attributes, "rehypePlugins");
52866
+ const pluginsAttribute = getAuthoritativeJsxAttribute(node.attributes, "rehypePlugins");
51291
52867
  if (!pluginsAttribute) return;
51292
52868
  const pluginsExpression = getAttributeExpression(pluginsAttribute);
51293
52869
  if (!pluginsExpression) return;
@@ -54722,6 +56298,22 @@ const isLikelyNumericExpression = (node) => {
54722
56298
  if (isNodeOfType(node, "MemberExpression") && isNodeOfType(node.property, "Identifier") && isNumericName(node.property.name)) return true;
54723
56299
  return false;
54724
56300
  };
56301
+ const isRenderedInsideIntrinsicDomHost = (node, scopes) => {
56302
+ let ancestor = node.parent;
56303
+ while (ancestor) {
56304
+ if (isNodeOfType(ancestor, "JSXAttribute")) {
56305
+ if (!(isNodeOfType(ancestor.name, "JSXIdentifier") && ancestor.name.name === "children")) return false;
56306
+ }
56307
+ if (isNodeOfType(ancestor, "JSXElement") && !isJsxFragmentElement(ancestor.openingElement, scopes)) {
56308
+ const elementName = ancestor.openingElement.name;
56309
+ if (!isNodeOfType(elementName, "JSXIdentifier")) return false;
56310
+ return HTML_TAGS.has(elementName.name) || SVG_TAGS.has(elementName.name);
56311
+ }
56312
+ if (isNodeOfType(ancestor, "CallExpression") || isNodeOfType(ancestor, "NewExpression") || isNodeOfType(ancestor, "VariableDeclarator") || isNodeOfType(ancestor, "AssignmentExpression") || isNodeOfType(ancestor, "Property") || isFunctionLike$1(ancestor) || isNodeOfType(ancestor, "Program")) return false;
56313
+ ancestor = ancestor.parent;
56314
+ }
56315
+ return false;
56316
+ };
54725
56317
  const rnNoFalsyAndRender = defineRule({
54726
56318
  id: "rn-no-falsy-and-render",
54727
56319
  title: "Numeric && renders bare zero",
@@ -54740,6 +56332,7 @@ const rnNoFalsyAndRender = defineRule({
54740
56332
  if (!(isNodeOfType(node.right, "JSXElement") || isNodeOfType(node.right, "JSXFragment"))) return;
54741
56333
  const parent = node.parent;
54742
56334
  if (!(isNodeOfType(parent, "JSXExpressionContainer") || isNodeOfType(parent, "LogicalExpression") && parent.operator === "&&")) return;
56335
+ if (isRenderedInsideIntrinsicDomHost(node, context.scopes)) return;
54743
56336
  const left = node.left;
54744
56337
  if (!left) return;
54745
56338
  if (!isLikelyNumericExpression(left)) return;
@@ -59772,7 +61365,7 @@ const roleSupportsAriaProps = defineRule({
59772
61365
  if (!ariaAttributes) return;
59773
61366
  const elementType = getElementType(node, context.settings);
59774
61367
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
59775
- const roleCandidates = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [getImplicitRole(node, elementType)].filter((role) => role !== null);
61368
+ const roleCandidates = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [getImplicitRole(node, elementType, context.scopes)].filter((role) => role !== null);
59776
61369
  if (roleCandidates === null || roleCandidates.length === 0) return;
59777
61370
  const supportedSets = [];
59778
61371
  for (const role of roleCandidates) {
@@ -61430,29 +63023,19 @@ const serverNoMutableModuleState = defineRule({
61430
63023
  });
61431
63024
  //#endregion
61432
63025
  //#region src/plugin/rules/server/server-sequential-independent-await.ts
61433
- const collectDeclaredNames = (declaration) => {
61434
- const names = /* @__PURE__ */ new Set();
61435
- if (!isNodeOfType(declaration, "VariableDeclaration")) return names;
61436
- for (const declarator of declaration.declarations ?? []) collectPatternNames(declarator.id, names);
61437
- return names;
61438
- };
61439
63026
  const declarationStartsWithAwait = (declaration) => {
61440
63027
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
61441
63028
  for (const declarator of declaration.declarations ?? []) if (isNodeOfType(declarator.init, "AwaitExpression")) return true;
61442
63029
  return false;
61443
63030
  };
61444
- const declarationReadsAnyName = (declaration, names) => {
61445
- if (names.size === 0) return false;
63031
+ const declarationReadsAnyPatternBinding = (declaration, patterns, context) => {
63032
+ if (patterns.length === 0) return false;
61446
63033
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
61447
- let didRead = false;
61448
63034
  for (const declarator of declaration.declarations ?? []) {
61449
63035
  if (!declarator.init) continue;
61450
- walkAst(declarator.init, (child) => {
61451
- if (didRead) return;
61452
- if (isNodeOfType(child, "Identifier") && names.has(child.name)) didRead = true;
61453
- });
63036
+ if (expressionReadsPatternBinding(declarator.init, patterns, context.scopes)) return true;
61454
63037
  }
61455
- return didRead;
63038
+ return false;
61456
63039
  };
61457
63040
  const GATE_LEADING_VERBS = new Set([
61458
63041
  "require",
@@ -61526,11 +63109,11 @@ const serverSequentialIndependentAwait = defineRule({
61526
63109
  const currentStatement = statements[statementIndex];
61527
63110
  if (!isNodeOfType(currentStatement, "VariableDeclaration")) continue;
61528
63111
  if (!declarationStartsWithAwait(currentStatement)) continue;
61529
- const declaredNames = collectDeclaredNames(currentStatement);
63112
+ const declaredPatterns = currentStatement.declarations.map((declarator) => declarator.id);
61530
63113
  const nextStatement = statements[statementIndex + 1];
61531
63114
  if (!isNodeOfType(nextStatement, "VariableDeclaration")) continue;
61532
63115
  if (!declarationStartsWithAwait(nextStatement)) continue;
61533
- if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
63116
+ if (declarationReadsAnyPatternBinding(nextStatement, declaredPatterns, context)) continue;
61534
63117
  if (declarationAwaitsExistingPromise(nextStatement)) continue;
61535
63118
  if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
61536
63119
  if (declarationAwaitsGate(currentStatement, context)) continue;
@@ -62551,7 +64134,7 @@ const tanstackStartNoNavigateInRender = defineRule({
62551
64134
  return false;
62552
64135
  };
62553
64136
  const isWiredAsEventHandler = (functionNode) => {
62554
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
64137
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
62555
64138
  if (!bindingIdentifier) return false;
62556
64139
  const bindingSymbol = context.scopes.symbolFor(bindingIdentifier);
62557
64140
  if (!bindingSymbol) return false;
@@ -62571,7 +64154,7 @@ const tanstackStartNoNavigateInRender = defineRule({
62571
64154
  return false;
62572
64155
  };
62573
64156
  const isSynchronouslyInvokedAnonymousWrapper = (functionNode) => {
62574
- if (getFunctionBindingIdentifier(functionNode)) return false;
64157
+ if (getFunctionBindingIdentifier$1(functionNode)) return false;
62575
64158
  const parent = functionNode.parent;
62576
64159
  if (!isNodeOfType(parent, "CallExpression")) return false;
62577
64160
  return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
@@ -68503,6 +70086,7 @@ const CROSS_FILE_RULE_IDS = new Set([
68503
70086
  "no-indeterminate-attribute",
68504
70087
  "no-locale-format-in-render",
68505
70088
  "no-match-media-in-state-initializer",
70089
+ "no-create-ref-in-function-component",
68506
70090
  "no-adjust-state-on-prop-change",
68507
70091
  "no-derived-state",
68508
70092
  "no-derived-state-effect",
@@ -68607,6 +70191,18 @@ const collectForwardedHookDependencies = ({ absoluteFilePath, staticImports }) =
68607
70191
  collectProgramDependencies(resolved.filePath, resolved.programNode, 4);
68608
70192
  }
68609
70193
  };
70194
+ const collectCreateRefDependencies = ({ absoluteFilePath, program }) => {
70195
+ attachParentReferences(program);
70196
+ const scopes = analyzeScopes(program);
70197
+ walkAst(program, (node) => {
70198
+ if (!isNodeOfType(node, "CallExpression") || !isReactApiCall(node, "createRef", scopes, {
70199
+ allowGlobalReactNamespace: true,
70200
+ allowUnboundBareCalls: true,
70201
+ resolveNamedAliases: true
70202
+ })) return;
70203
+ isCreateRefResultWriteOnly(node, absoluteFilePath, scopes);
70204
+ });
70205
+ };
68610
70206
  const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
68611
70207
  const namedUseReducerLocals = /* @__PURE__ */ new Set();
68612
70208
  const reactObjectLocals = /* @__PURE__ */ new Set();
@@ -68695,6 +70291,7 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
68695
70291
  ["no-indeterminate-attribute", collectNearestManifestDependencies],
68696
70292
  ["no-locale-format-in-render", collectNearestManifestDependencies],
68697
70293
  ["no-match-media-in-state-initializer", collectNearestManifestDependencies],
70294
+ ["no-create-ref-in-function-component", collectCreateRefDependencies],
68698
70295
  ["no-adjust-state-on-prop-change", collectEffectValueHelperDependencies],
68699
70296
  ["no-derived-state", collectEffectValueHelperDependencies],
68700
70297
  ["no-derived-state-effect", collectEffectValueHelperDependencies],