Skip to content

Commit

Permalink
fix(useSortedClasses): should suggest code fixes that match the JSX q…
Browse files Browse the repository at this point in the history
…uote style of the formatter (#4857)
  • Loading branch information
lucasweng authored Jan 8, 2025
1 parent eedb22e commit 4007147
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

- [noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) now handles `JsxAttributeInitializerClause`, ensuring that fragments inside expressions like `<A b=<></> />` are preserved. ([#4208](https://github.com/biomejs/biome/issues/4208)). Contributed by @MaxtuneLee

- [useSortedClasses](https://biomejs.dev/linter/rules/use-sorted-classes/) now suggests code fixes that match the JSX quote style of the formatter ([#4855](https://github.com/biomejs/biome/issues/4855)). Contributed by @lucasweng

### Parser

#### Bug fixes
Expand Down
8 changes: 8 additions & 0 deletions crates/biome_analyze/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct RuleContext<'a, R: Rule> {
file_path: &'a Path,
options: &'a R::Options,
preferred_quote: &'a PreferredQuote,
preferred_jsx_quote: &'a PreferredQuote,
jsx_runtime: Option<JsxRuntime>,
}

Expand All @@ -33,6 +34,7 @@ where
file_path: &'a Path,
options: &'a R::Options,
preferred_quote: &'a PreferredQuote,
preferred_jsx_quote: &'a PreferredQuote,
jsx_runtime: Option<JsxRuntime>,
) -> Result<Self, Error> {
let rule_key = RuleKey::rule::<R>();
Expand All @@ -45,6 +47,7 @@ where
file_path,
options,
preferred_quote,
preferred_jsx_quote,
jsx_runtime,
})
}
Expand Down Expand Up @@ -168,6 +171,11 @@ where
self.preferred_quote
}

/// Returns the preferred JSX quote that should be used when providing code actions
pub fn as_preferred_jsx_quote(&self) -> &PreferredQuote {
self.preferred_jsx_quote
}

/// Attempts to retrieve a service from the current context
///
/// ```no_test
Expand Down
7 changes: 7 additions & 0 deletions crates/biome_analyze/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub struct AnalyzerConfiguration {
/// Allows to choose a different quote when applying fixes inside the lint rules
pub preferred_quote: PreferredQuote,

/// Allows to choose a different JSX quote when applying fixes inside the lint rules
pub preferred_jsx_quote: PreferredQuote,

/// Indicates the type of runtime or transformation used for interpreting JSX.
pub jsx_runtime: Option<JsxRuntime>,
}
Expand Down Expand Up @@ -117,6 +120,10 @@ impl AnalyzerOptions {
pub fn preferred_quote(&self) -> &PreferredQuote {
&self.configuration.preferred_quote
}

pub fn preferred_jsx_quote(&self) -> &PreferredQuote {
&self.configuration.preferred_jsx_quote
}
}

#[derive(Debug, Default)]
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_analyze/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ impl<L: Language + Default> RegistryRule<L> {
let query_result = <R::Query as Queryable>::unwrap_match(params.services, query_result);
let globals = params.options.globals();
let preferred_quote = params.options.preferred_quote();
let preferred_jsx_quote = params.options.preferred_jsx_quote();
let jsx_runtime = params.options.jsx_runtime();
let options = params.options.rule_options::<R>().unwrap_or_default();
let ctx = match RuleContext::new(
Expand All @@ -409,6 +410,7 @@ impl<L: Language + Default> RegistryRule<L> {
&params.options.file_path,
&options,
preferred_quote,
preferred_jsx_quote,
jsx_runtime,
) {
Ok(ctx) => ctx,
Expand Down
4 changes: 4 additions & 0 deletions crates/biome_analyze/src/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ where
fn diagnostic(&self) -> Option<AnalyzerDiagnostic> {
let globals = self.options.globals();
let preferred_quote = self.options.preferred_quote();
let preferred_jsx_quote = self.options.preferred_jsx_quote();
let options = self.options.rule_options::<R>().unwrap_or_default();
let ctx = RuleContext::new(
&self.query_result,
Expand All @@ -355,6 +356,7 @@ where
&self.options.file_path,
&options,
preferred_quote,
preferred_jsx_quote,
self.options.jsx_runtime(),
)
.ok()?;
Expand Down Expand Up @@ -386,6 +388,7 @@ where
&self.options.file_path,
&options,
self.options.preferred_quote(),
self.options.preferred_jsx_quote(),
self.options.jsx_runtime(),
)
.ok();
Expand Down Expand Up @@ -435,6 +438,7 @@ where
&self.options.file_path,
&options,
self.options.preferred_quote(),
self.options.preferred_jsx_quote(),
self.options.jsx_runtime(),
)
.ok();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ impl Rule for UseSortedClasses {
mutation.replace_node(string_literal.clone(), replacement);
}
AnyClassStringLike::JsxString(jsx_string_node) => {
let replacement = jsx_string(if ctx.as_preferred_quote().is_double() {
let replacement = jsx_string(if ctx.as_preferred_jsx_quote().is_double() {
js_string_literal(state)
} else {
js_string_literal_single_quotes(state)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div class="content-[''] absolute">Hello</div>;
<div class='top-0 absolute'>Hello</div>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: issue_4855.jsx
snapshot_kind: text
---
# Input
```jsx
<div class="content-[''] absolute">Hello</div>;
<div class='top-0 absolute'>Hello</div>;

```

# Diagnostics
```
issue_4855.jsx:1:12 lint/nursery/useSortedClasses FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! These CSS classes should be sorted.
> 1 │ <div class="content-[''] absolute">Hello</div>;
│ ^^^^^^^^^^^^^^^^^^^^^^^
2 │ <div class='top-0 absolute'>Hello</div>;
3 │
i Unsafe fix: Sort the classes.
1 │ - <div·class="content-['']·absolute">Hello</div>;
1 │ + <div·class="absolute·content-['']">Hello</div>;
2 2 │ <div class='top-0 absolute'>Hello</div>;
3 3 │
```

```
issue_4855.jsx:2:12 lint/nursery/useSortedClasses FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! These CSS classes should be sorted.
1 │ <div class="content-[''] absolute">Hello</div>;
> 2 │ <div class='top-0 absolute'>Hello</div>;
│ ^^^^^^^^^^^^^^^^
3 │
i Unsafe fix: Sort the classes.
1 1 │ <div class="content-[''] absolute">Hello</div>;
2 │ - <div·class='top-0·absolute'>Hello</div>;
2 │ + <div·class="absolute·top-0">Hello</div>;
3 3 │
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json",
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double"
}
},
"linter": {
"rules": {
"nursery": {
"useSortedClasses": {
"level": "error",
"options": {
"attributes": ["customClassAttribute"],
"functions": ["clsx", "tw", "tw.*"]
}
}
}
}
}
}
1 change: 1 addition & 0 deletions crates/biome_service/src/file_handlers/css.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ impl ServiceLanguage for CssLanguage {
.unwrap_or_default(),
globals: Vec::new(),
preferred_quote,
preferred_jsx_quote: Default::default(),
jsx_runtime: None,
};

Expand Down
14 changes: 14 additions & 0 deletions crates/biome_service/src/file_handlers/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ impl ServiceLanguage for JsLanguage {
)
})
.unwrap_or_default();
let preferred_jsx_quote = global
.and_then(|global| {
global.languages.javascript.formatter.jsx_quote_style.map(
|quote_style: QuoteStyle| {
if quote_style == QuoteStyle::Single {
PreferredQuote::Single
} else {
PreferredQuote::Double
}
},
)
})
.unwrap_or_default();

let mut jsx_runtime = None;
let mut globals = Vec::new();
Expand Down Expand Up @@ -276,6 +289,7 @@ impl ServiceLanguage for JsLanguage {
.unwrap_or_default(),
globals,
preferred_quote,
preferred_jsx_quote,
jsx_runtime,
};

Expand Down
1 change: 1 addition & 0 deletions crates/biome_service/src/file_handlers/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl ServiceLanguage for JsonLanguage {
.unwrap_or_default(),
globals: vec![],
preferred_quote: PreferredQuote::Double,
preferred_jsx_quote: Default::default(),
jsx_runtime: Default::default(),
};
AnalyzerOptions {
Expand Down
1 change: 1 addition & 0 deletions crates/biome_test_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub fn create_analyzer_options(
rules: AnalyzerRules::default(),
globals: vec![],
preferred_quote: PreferredQuote::Double,
preferred_jsx_quote: PreferredQuote::Double,
jsx_runtime: Some(JsxRuntime::Transparent),
};
let options_file = input_file.with_extension("options.json");
Expand Down

0 comments on commit 4007147

Please sign in to comment.