Skip to content

Disable unicorn/prefer-scoped-selector (#279)#297

Merged
twschiller merged 1 commit into
mainfrom
disable-unicorn-prefer-scoped-selector
Jun 22, 2026
Merged

Disable unicorn/prefer-scoped-selector (#279)#297
twschiller merged 1 commit into
mainfrom
disable-unicorn-prefer-scoped-selector

Conversation

@twschiller

Copy link
Copy Markdown
Contributor

Part of the eslint-plugin-unicorn ratchet (#279).

Verdict: turn off, don't ratchet

unicorn/prefer-scoped-selector flags 64 sites and wants
el.querySelector("foo")el.querySelector(":scope foo"). That rewrite
is only behavior-preserving when the receiver is an Element. This
codebase's shadow-piercing scanners take root: ParentNode and run against
Document, ShadowRoot, and DocumentFragment receivers at runtime, where
:scope is not equivalent — verified in jsdom:

Receiver qsa("*") qsa(":scope *")
Element 4 4 ✓
Document 10 9 (drops <html>)
ShadowRoot 3 0 — silently empty
DocumentFragment 1 0 — silently empty

:scope noscript on a ShadowRoot → 0 (vs 1); comma-lists break partially
(:scope p, span → 1 vs 2). So mechanically applying the rule's suggestion
would silently disable defenses inside shadow DOMnoscript-strip,
hidden-text-strip, unicode-invisibles-strip, trust-badge-annotate,
closed-shadow-root-annotate, and others that scan root: ParentNode.

The Element-receiver sites where :scope is safe are all single-compound
selectors (*, a[href], img, picture) with no descendant combinator, so
there's no correctness upside there either. The rule can't narrow to Element
receivers or to combinator-bearing selectors, so it goes off (per the
"disable no-plan rules, don't leave noisy warnings" convention) rather than
staying warn.

Config-only change; no source edits. bun run check (0 errors) and
typecheck pass.

🤖 Generated with Claude Code

The rule's `:scope` rewrite is unsound for this codebase's dominant
query-receiver type. It assumes the receiver is an Element, where
`el.querySelectorAll("foo")` and `el.querySelectorAll(":scope foo")`
are equivalent. But the shadow-piercing scanners take `root: ParentNode`
and run against `Document`, `ShadowRoot`, and `DocumentFragment` at
runtime, where `:scope` is NOT equivalent:

  - ShadowRoot / DocumentFragment: `:scope` matches nothing, so
    `:scope *` / `:scope noscript` silently return empty.
  - Document: `:scope *` excludes `<html>`.

Mechanically applying the suggestion would silently disable defenses
inside shadow DOM (noscript-strip, hidden-text-strip,
unicode-invisibles-strip, trust-badge-annotate,
closed-shadow-root-annotate, …). The Element-receiver sites where
`:scope` is safe are all single-compound selectors with no descendant
combinator, so there's no correctness upside there either. The rule
can't narrow to Element receivers or combinator selectors, so it goes
`off` rather than `warn`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-browser-shield-demo-site Ready Ready Preview, Comment Jun 22, 2026 2:55pm

Request Review

@twschiller twschiller merged commit 7378527 into main Jun 22, 2026
7 checks passed
@twschiller twschiller deleted the disable-unicorn-prefer-scoped-selector branch June 22, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant