Preserve relativeSelector: false inside :has() subselectors

MA
4d ago
+119-3

With relativeSelector: false, :has() currently forces nested selectors back into relative mode. For example, .p:has(+ :is(.a)) fails to match a .p followed by .a, and .p:has(+ .a:not(.b)) can incorrectly match a sibling that has both classes.

Apply relative matching only to the outer :has() argument. Nested functional selectors retain the caller's option, including aliases and :nth-child(... of S). Default and explicitly enabled relative matching keep their existing behavior.

Related #1930; this focuses on the explicit false option.

Tested:

  • Builds and full Vitest suite on Node 20.20.2, 22.23.4, 24.22.1 and 26.12.1: 237 passed, one existing skipped test each.
  • ESLint, test type checking and Biome.
  • Eight regressions fail on unchanged main. Local Chromium comparisons pass with the option disabled, with default-mode results unchanged.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed nested selectors inside :has so they respect the configured relative-selector behavior, including when relative selectors are disabled.
    • Improved consistency of :has results across compiled selectors, is, and selectAll, with caching enabled or disabled.
    • Preserved the existing behavior when relative selectors are enabled by default or explicitly enabled.