Skip to content

Commit 507f71e

Browse files
authored
feat(cli): support rule without group in --only and --skip (#6004)
1 parent 716a6d1 commit 507f71e

File tree

5 files changed

+95
-35
lines changed

5 files changed

+95
-35
lines changed

.changeset/bright-teeth-like.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@biomejs/biome": minor
3+
---
4+
5+
The CLI options `--only` and `--skip` now accept rule and action names without prefixing the group name.
6+
7+
Previously `--only=noDebugger` was rejected.
8+
You had to add the group name: `--only=suspicious/noDebugger`.

crates/biome_cli/tests/commands/lint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2803,7 +2803,7 @@ fn lint_only_missing_group() {
28032803
Args::from(["lint", "--only=noDebugger", file_path.as_str()].as_slice()),
28042804
);
28052805

2806-
assert!(result.is_err(), "run_cli returned {result:?}");
2806+
assert!(result.is_ok(), "run_cli returned {result:?}");
28072807

28082808
assert_cli_snapshot(SnapshotPayload::new(
28092809
module_path!(),
Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: crates/biome_cli/tests/snap_test.rs
3-
expression: content
3+
expression: redactor(content)
44
---
55
## `check.js`
66

@@ -9,17 +9,8 @@ for(;true;);
99
1010
```
1111

12-
# Termination Message
12+
# Emitted Messages
1313

1414
```block
15-
flags/invalid ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16-
17-
× Failed to parse CLI arguments.
18-
19-
Caused by:
20-
couldn't parse `noDebugger`: This group doesn't exist. Use the syntax `<group>/<rule>` to specify a
21-
rule.
22-
23-
24-
15+
Checked 1 file in <TIME>. No fixes applied.
2516
```

crates/biome_configuration/src/analyzer/mod.rs

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub mod linter;
44
use crate::analyzer::assist::Actions;
55
pub use crate::analyzer::linter::*;
66
use biome_analyze::options::RuleOptions;
7-
use biome_analyze::{FixKind, RuleFilter};
7+
use biome_analyze::{FixKind, RuleCategory, RuleFilter};
88
use biome_deserialize::{
99
Deserializable, DeserializableType, DeserializableValue, DeserializationContext, Merge,
1010
};
@@ -493,35 +493,94 @@ impl<'a> From<&'a RuleSelector> for RuleFilter<'static> {
493493
impl FromStr for RuleSelector {
494494
type Err = &'static str;
495495
fn from_str(selector: &str) -> Result<Self, Self::Err> {
496-
let selector = selector
497-
.strip_prefix("lint/")
498-
.or_else(|| selector.strip_prefix("assist/"))
499-
.unwrap_or(selector);
500-
501-
if let Some((group_name, rule_name)) = selector.split_once('/') {
502-
if let Ok(group) = linter::RuleGroup::from_str(group_name) {
503-
if let Some(rule_name) = Rules::has_rule(group, rule_name) {
504-
Ok(Self::Rule(group.as_str(), rule_name))
496+
let (selector_kind, selector) = if let Some(lint_selector) = selector.strip_prefix("lint/")
497+
{
498+
(Some(RuleCategory::Lint), lint_selector)
499+
} else if let Some(assist_selector) = selector.strip_prefix("assist/") {
500+
(Some(RuleCategory::Action), assist_selector)
501+
} else {
502+
(None, selector)
503+
};
504+
505+
// If `optional_group_name` is set, then `rule_or_group_name` is a group.
506+
// Otherwise it is either a rule or a group.
507+
let (optional_group_name, rule_or_group_name) =
508+
if let Some((group_name, rule_name)) = selector.split_once('/') {
509+
(Some(group_name), rule_name)
510+
} else {
511+
(None, selector)
512+
};
513+
514+
if let Ok(rule_name) = linter::RuleName::from_str(rule_or_group_name) {
515+
let static_group_name = rule_name.group().as_str();
516+
// TODO: remove the `style/useNamingConvention` exception,
517+
// once we have promoted the GraphQL `useNamingConvention` rule.
518+
//
519+
// See https://github.com/biomejs/biome/issues/6018
520+
if optional_group_name.is_none_or(|name| name == static_group_name)
521+
|| (rule_or_group_name == "useNamingConvention"
522+
&& optional_group_name == Some("style"))
523+
{
524+
if matches!(selector_kind, None | Some(RuleCategory::Lint)) {
525+
// TODO: remove the `style/useNamingConvention` exception,
526+
// once we have promoted the GraphQL `useNamingConvention` rule.
527+
let static_group_name = if rule_or_group_name == "useNamingConvention"
528+
&& optional_group_name == Some("style")
529+
{
530+
"style"
531+
} else {
532+
static_group_name
533+
};
534+
Ok(Self::Rule(static_group_name, rule_name.as_str()))
505535
} else {
506-
Err("This rule doesn't exist.")
536+
Err(
537+
"This is a lint rule and not an assist rule. Use the `lint/` prefix or remove the prefix.",
538+
)
507539
}
508-
} else if let Ok(group) = assist::RuleGroup::from_str(group_name) {
509-
if let Some(rule_name) = Actions::has_rule(group, rule_name) {
510-
Ok(Self::Rule(group.as_str(), rule_name))
540+
} else {
541+
Err("This rule is under a different group.")
542+
}
543+
} else if let Ok(rule_name) = assist::ActionName::from_str(rule_or_group_name) {
544+
let static_group_name = rule_name.group().as_str();
545+
if optional_group_name.is_none_or(|name| name == static_group_name) {
546+
if matches!(selector_kind, None | Some(RuleCategory::Action)) {
547+
Ok(Self::Rule(static_group_name, rule_name.as_str()))
511548
} else {
512-
Err("This rule doesn't exist.")
549+
Err(
550+
"This is an assist rule and not a lint rule. Use the `assist/` prefix or remove the prefix.",
551+
)
513552
}
514553
} else {
515-
Err("This rule doesn't exist.")
554+
Err("This action is under a different group.")
516555
}
517-
} else {
518-
if let Ok(group) = linter::RuleGroup::from_str(selector) {
519-
return Ok(Self::Group(group.as_str()));
556+
} else if let Some(group_name) = optional_group_name {
557+
Err(if linter::RuleGroup::from_str(group_name).is_ok() {
558+
"This rule doesn't exist."
559+
} else if assist::RuleGroup::from_str(group_name).is_ok() {
560+
"This action doesn't exist."
561+
} else {
562+
"This rule or action doesn't exist."
563+
})
564+
} else if let Ok(group) = linter::RuleGroup::from_str(rule_or_group_name) {
565+
if matches!(selector_kind, None | Some(RuleCategory::Lint)) {
566+
Ok(Self::Group(group.as_str()))
567+
} else {
568+
Err(
569+
"This is a lint group and not an assist group. Use the `lint/` prefix or remove the prefix.",
570+
)
520571
}
521-
if let Ok(group) = assist::RuleGroup::from_str(selector) {
522-
return Ok(Self::Group(group.as_str()));
572+
} else if let Ok(group) = assist::RuleGroup::from_str(rule_or_group_name) {
573+
if matches!(selector_kind, None | Some(RuleCategory::Action)) {
574+
Ok(Self::Group(group.as_str()))
575+
} else {
576+
Err(
577+
"This is an assist group and not a lint group. Use the `assist/` prefix or remove the prefix.",
578+
)
523579
}
524-
Err("This group doesn't exist. Use the syntax `<group>/<rule>` to specify a rule.")
580+
} else if rule_or_group_name.bytes().all(|c| c.is_ascii_lowercase()) {
581+
Err("This group doesn't exist.")
582+
} else {
583+
Err("This rule name or action name doesn't exist.")
525584
}
526585
}
527586
}

crates/biome_migrate/src/analyzers/rule_mover.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ impl Rule for RuleMover {
7272
if let Ok(new_rule) = RuleName::from_str(rule_name) {
7373
// TODO: remove the `useNamingConvention` exception,
7474
// once we have promoted the GraphQL `useNamingConvention` rule
75+
//
76+
// See https://github.com/biomejs/biome/issues/6018
7577
if new_rule.group() != current_group && rule_name != "useNamingConvention" {
7678
result.push(State {
7779
rule_node,

0 commit comments

Comments
 (0)