Skip to content

feat(scraps): Add rule against component and DOM selectors - #128234

Draft
TkDodo wants to merge 9 commits into
masterfrom
tkdodo/feat/no-dom-coupling
Draft

TkDodo wants to merge 9 commits into
masterfrom
tkdodo/feat/no-dom-coupling

Conversation

@TkDodo

@TkDodo TkDodo commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

lint rule @sentry/scraps/no-dom-coupling to reject child selectors, component selectors, universal target selectors etc. in styled() and css() so that we don’t write css that couples us to implementation details anymore!

815 existing violations, so added to the incubator. Turned off for scraps because we do this internally and it’s actually fine because it’s scoped.

Closes DE-1714

@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Oct 20, 2026
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

📊 Type Coverage Diff

✅ no issues found

@TkDodo

TkDodo commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread static/oxlint/eslintPluginScraps/src/rules/noDomCoupling.ts
Comment thread static/oxlint/eslintPluginScraps/src/rules/noDomCoupling.ts
Comment thread static/oxlint/eslintPluginScraps/src/ast/utils/selectors.ts
@TkDodo

TkDodo commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a14ed25. Configure here.

@linear-code

linear-code Bot commented Oct 20, 2026

Copy link
Copy Markdown

DE-1714

@priscilawebdev

Copy link
Copy Markdown
Member

The rule misses styles returned from switch cases. Could we check those too, so it catches DOM selectors in non-core style helpers?

@priscilawebdev priscilawebdev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉🚀🙌

@sentry

sentry Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Sentry Snapshot Testing

Name Added Removed Changed Renamed Unchanged Skipped Status
sentry-frontend
sentry-frontend
0 0 0 0 754 0 ✅ Unchanged

⚙️ sentry-frontend Snapshot Settings

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! Just a few suggestions, I don't think they're blocking at all.

I ran my proto lint cleanup skill and it came out with #128361. I leave that for your perusal if you want. 🙂

schema: [],
messages: {
forbidden:
'Do not select components, DOM elements, universal targets, or unscoped children. Use an explicit class, ID, or attribute target instead.',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Feature] You might want to add a data type so the message can specifically sway which forbidden thing is being targeted. It'll help folks who aren't super familiar with what the differences are.

Also, it's good to have a brief mention of "why" in the message, so the rule feels like relevant issue-reporting and not just griping.

Starting to wordsmith a bit (?s are my actual not-sure-which-es):

Suggested change
'Do not select components, DOM elements, universal targets, or unscoped children. Use an explicit class, ID, or attribute target instead.',
'{{target}} is an overly (broad? deep?) target that relies on implementation details. Use an explicit class, ID, or attribute target instead.',

if (node.type === 'tag' || node.type === 'universal') {
forbidden = true;
}
if (node.type === 'combinator' && node.value.trim() === '>') {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Bug] Only > checks whether the subject is scoped, so an unscoped pseudo-class subject after any other combinator gets through:

css`& > :first-child {color: red;}`; // reported
css`& :first-child {color: red;}`; // not reported
css`& :not(.a) {color: red;}`; // not reported
css`& + :first-child {color: red;}`; // not reported

The descendant cases couple to even more of the DOM than the > one does. Should this check the subject after every combinator?

return [''];
}
return expression.body.type === 'BlockStatement'
? returnedValues(expression.body).flatMap(resolve)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Bug] A block-bodied function with no return doesn't give back any candidate. So nothing after it gets checked:

styled.div`${p => { f(p); }} button {color: red;}`; // not reported
styled.div`${p => { return ''; }} button {color: red;}`; // reported

Falling back to [''] when there are no returns would keep checking the static text.

This branch was successfully deployed

1 active deployment
Preview — 88923b9d Deployed Oct 21, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants