Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📎 Implment no-duplicate-selectors #2514

Open
Tracked by #2511
togami2864 opened this issue Apr 18, 2024 · 10 comments · May be fixed by #2660
Open
Tracked by #2511

📎 Implment no-duplicate-selectors #2514

togami2864 opened this issue Apr 18, 2024 · 10 comments · May be fixed by #2660
Assignees
Labels
A-Analyzer Area: analyzer A-Linter Area: linter L-CSS Language: CSS S-Help-wanted Status: you're familiar with the code base and want to help the project

Comments

@togami2864
Copy link
Contributor

togami2864 commented Apr 18, 2024

Description

Implmenet no-duplicate-selectors

Want to contribute? Lets you know you are interested! We will assign you to the issue to prevent several people to work on the same issue. Don't worry, we can unassign you later if you are no longer interested in the issue! Read our contributing guide and analyzer contributing guide.

@togami2864 togami2864 changed the title no-duplicate-selectors 📎 Implment no-duplicate-selectors Apr 18, 2024
@togami2864 togami2864 added S-Help-wanted Status: you're familiar with the code base and want to help the project A-Linter Area: linter L-CSS Language: CSS A-Analyzer Area: analyzer labels Apr 18, 2024
@abidjappie
Copy link
Contributor

Hi, I would like to contribute to this issue! 🙇

@abidjappie
Copy link
Contributor

Still working on this one, will do a status update as soon as I can 🙇

  • Wrote a visitor to return the Selector groupings in a sheet.

Since we need to compare the all the selectors in the sheet against each other, as well as being able to compare whether selectors are equivalent I need to do some things to "resolve" the selector.

@abidjappie
Copy link
Contributor

abidjappie commented Apr 30, 2024

I created a draft PR (#2660)

Working on trying to get the selector resolver to properly resolve the & case, and then to work with AtRule.
I think the implementation is going to end up much larger than I anticipated, mainly to resolve selectors so they can be compared for equivalents.

I want to eventually remove the Visitor but right now it's the easiest way to get the nodes of interest, my other option is to get the entire list and handle it (seems like a lot of traversing for the run).

If there is some way to persist State between multiple runs that I can use to track the resolved selectors then it may be easier to compare by just fetching the upper level CSS rules. 🤔

@abidjappie abidjappie linked a pull request May 7, 2024 that will close this issue
@abidjappie
Copy link
Contributor

Update: working on another solution that does not rely on recursion (see the PR for context). Will attempt to reduce the scope covered by it so it's easier to review.

@abidjappie
Copy link
Contributor

I think having the new semantic model will help with my solution to this. Hope to continue with it if there's no rush, but feel free to assign to someone else if they really need this!

@togami2864
Copy link
Contributor Author

Thanks @abidjappie. Feel free to ping me if you have the question about the model. Also, I'll write the documentation.

About this rule, you can use model.rules() to get all top-level css rules, iterate over them recursibly and check all selectors.

@abidjappie
Copy link
Contributor

@togami2864 thank you!

I played around with the model yesterday and it definitely makes it a lot easier since I don't have to handle all the types of Selectors myself and I can just use the name directly for comparison.

I used type Query = SemanticServices to query it.

"Resolving" the selectors to their equivalent is still the main challenge, and I've found a few ways to achieve it but I still need to get it to pass the test cases.

I'm using a stack to avoid recursion (I was advised to avoid recursion before)!

@abidjappie
Copy link
Contributor

abidjappie commented Sep 25, 2024

I think that losing some of the data to the semantic model is going to make resolving them a bit difficult orz, e.g.

// selector group 1
.a, .b { }

// selector group 2
.d .e { }

Which would select:

<!-- group 1 -->
<element class="a">
  ...
</element>
<element class="b">
  ...
</element>

<!-- group 2 -->
<element class="d">
  <element class="e">
  ...
  </element>
</element>

These are both represented as (not true code):

[{
  Selectors: [ ".a", ".b" ]
},{
  Selectors: [ ".d", ".e" ]
}]

@togami2864
Copy link
Contributor Author

Currently, the model does not classify conditions in detail.

match selector {
AnyCssSelector::CssComplexSelector(s) => {
if let Ok(l) = s.left() {
self.add_selector_event(Cow::Borrowed(&l.text()), l.range());
}
if let Ok(r) = s.right() {
self.add_selector_event(Cow::Borrowed(&r.text()), r.range());
}
}
AnyCssSelector::CssCompoundSelector(selector) => {
let selector_text = selector.text();
if selector_text == ROOT_SELECTOR {
self.stash.push_back(SemanticEvent::RootSelectorStart);
self.is_in_root_selector = true;
}
self.add_selector_event(Cow::Borrowed(&selector_text), selector.range())
}
_ => {}

@abidjappie
About descendant Selector, how about representing like the following?

[{
  // .a .b {} (Descendant Selector)
  Selectors: [ ".a .b" ]
},{
  // .d, .e (Grouping selector)
  Selectors: [ ".d", ".e" ]
}]

And are there any other selectors or patterns need to be addressed besides the descendant combinator when implementing this rule?

@abidjappie
Copy link
Contributor

abidjappie commented Oct 4, 2024

@togami2864 thanks for the response.

How about something like this:
(Selectors is the same representation you mentioned above... and Resolved just demonstrates how I intend to use it)

/* Descendant combinator (space) */
.foo .bar  {}
/* Selectors: [".foo .bar"] */
/* Resolved: [".foo .bar"] */

/* Child combinator (`>`) */
/* Probably treated same as child, column, next-sibling, etc. */
.foo > .bar  {}
.foo || .bar  {}
.foo + .bar  {}
.foo ~ .bar  {}
/* Selectors: [".foo>.bar"] - treat `>`, ` `, `||` etc. similarly */
/* Resolved: [".foo>.bar"] */

/* Selector list (comma) */
.a, .b { div {} }
/* Selectors: [".a",".b"], ["div"] */
/* Resolved: [".a", ".b", ".a div", ".b div"] */

/* `&` nesting selector */
.foo { .bar &:hover }
/* Selectors: [".foo"], [".bar &:hover"] */
/* Resolved: [".foo", ".bar .foo:hover"] */

/* Attribute, Class, ID selector */
type[attribute].class#id, div {}
/* Selectors: ["type[attribute].class#id", "div"] */
/* The details probably don't matter for these */

I think these cases would probably suffice for this rule. I'm not sure these are the kind of details we would want to add into the semantic model though?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Analyzer Area: analyzer A-Linter Area: linter L-CSS Language: CSS S-Help-wanted Status: you're familiar with the code base and want to help the project
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants