diff --git a/CHANGELOG.md b/CHANGELOG.md index 486642aee7a1..b7ec950957ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,39 +21,36 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b #### New features -- Add a new option `--rule` to the command `biome lint` ([#58](https://github.com/biomejs/biome/issues/58)). +- Add two new options `--only` and `--skip` to the command `biome lint` ([#58](https://github.com/biomejs/biome/issues/58)). - This new option allows you to execute a single rule or a rule group. - This option is convenient to test a rule or apply the code fixes of a single rule. - - For example, you can execute the `style/useNamingConvention` rule on the working directory: + The `--only` option allows you to run a given rule or rule group, + For example, the following command runs only the `style/useNamingConvention` and `style/noInferrableTypes` rules. + If the rule is disabled in the configuration, then its severity level is set to `error` for a recommended rule or `warn` otherwise. ```shell - biome lint --rule=style/useNamingConvention ./ + biome lint --only=style/useNamingConvention --only=style/noInferrableTypes ``` - If the rule has a code action (autofix), you can use `--apply` to apply the fix: + Passing a group does not change the severity level of the rules in the group. + All the disabled rules in the group will remain disabled. + To ensure that the group is run, the `recommended` field of the group is enabled. + The `nursery` group cannot be passed, as no rules are enabled by default in the nursery group. + + The `--skip` option allows you to skip the execution of a given group or a given rule. + For example, the following command skips the `style` group and the `suspicious/noExplicitAny` rule. ```shell - biome lint --rule=style/useNamingConvention --apply ./ + biome lint --skip=style --skip=suspicious/noExplicitAny ``` - The option takes the rule options in the Biome configuration file into account. - Only, the severity level of the rule is overridden by its default value, - i.e. `error` for a recommended rule or `warn` otherwise. - - You can also run a group of rules: + You can also use `--only` and `--skip` together. + The following command executes only the rules from the `style` group, but the `style/useNamingConvention` rule. ```shell - biome lint --rule=suspicious src/main.js + biome lint --only=style --skip=style/useNamingConvention ``` - In this case, the severity level of a rule is not overridden. - Thus, the disabled rules stay disabled. - To ensure that the group is run, the `recommended` field of the group is turned on. - The `nursery` group cannot be passed because no rules are enabled in the nursery group by default. - - The option is compatible with other options such as `--apply`, `--apply-unsafe` and `--reporter`. + These options are compatible with other options such as `--write` (previously `--apply`), and `--reporter`. Contributed by @Conaclos diff --git a/crates/biome_analyze/src/lib.rs b/crates/biome_analyze/src/lib.rs index 520e20d137b3..5d6265004aac 100644 --- a/crates/biome_analyze/src/lib.rs +++ b/crates/biome_analyze/src/lib.rs @@ -795,6 +795,19 @@ impl<'a> RuleFilter<'a> { } } } + + /// Returns `true` if the current rule filter is more general than `other`. + pub fn contains(self, other: Self) -> bool { + match self { + RuleFilter::Group(group) => group == other.group(), + RuleFilter::Rule(group, rule) => match other { + RuleFilter::Group(_) => false, + RuleFilter::Rule(other_group, other_rule) => { + group == other_group && rule == other_rule + } + }, + } + } } impl<'a> Debug for RuleFilter<'a> { diff --git a/crates/biome_cli/src/commands/lint.rs b/crates/biome_cli/src/commands/lint.rs index b0beaa001e65..52a247657ed6 100644 --- a/crates/biome_cli/src/commands/lint.rs +++ b/crates/biome_cli/src/commands/lint.rs @@ -33,7 +33,8 @@ pub(crate) struct LintCommandPayload { pub(crate) vcs_configuration: Option, pub(crate) files_configuration: Option, pub(crate) paths: Vec, - pub(crate) rule: Option, + pub(crate) only: Vec, + pub(crate) skip: Vec, pub(crate) stdin_file_path: Option, pub(crate) staged: bool, pub(crate) changed: bool, @@ -54,7 +55,8 @@ pub(crate) fn lint(session: CliSession, payload: LintCommandPayload) -> Result<( cli_options, mut linter_configuration, mut paths, - rule, + only, + skip, stdin_file_path, vcs_configuration, files_configuration, @@ -67,6 +69,15 @@ pub(crate) fn lint(session: CliSession, payload: LintCommandPayload) -> Result<( } = payload; setup_cli_subscriber(cli_options.log_level, cli_options.log_kind); + if !skip.is_empty() { + if let Some(selector) = only.iter().find(|selector| skip.contains(selector)) { + return Err(CliDiagnostic::incompatible_arguments( + format!("--only={selector}"), + format!("--skip={selector}"), + )); + } + } + let fix_file_mode = determine_fix_file_mode( FixFileModeOptions { apply, @@ -161,7 +172,8 @@ pub(crate) fn lint(session: CliSession, payload: LintCommandPayload) -> Result<( Execution::new(TraversalMode::Lint { fix_file_mode, stdin, - rule, + only, + skip, }) .set_report(&cli_options), session, diff --git a/crates/biome_cli/src/commands/mod.rs b/crates/biome_cli/src/commands/mod.rs index baae4ba71af8..736af1fce8ea 100644 --- a/crates/biome_cli/src/commands/mod.rs +++ b/crates/biome_cli/src/commands/mod.rs @@ -201,11 +201,21 @@ pub enum BiomeCommand { /// /// - When a rule group is passed, the `recommended` flag is enabled, but if the `all` flag is enabled. /// - /// Example: `biome lint --rule=correctness/noUnusedVariables` + /// Example: `biome lint --only=correctness/noUnusedVariables --only=suspicious` + #[bpaf(long("only"), argument("GROUP|RULE"))] + only: Vec, + + /// Skip the given rule or rule group. + /// + /// The option overrides the Biome configuration file as follows: + /// + /// - When a rule is passed, its severity level is set to `off'. + /// + /// - When a rule group is passed, all rules of the group are skipped. /// - /// Example: `biome lint --rule=suspicious` - #[bpaf(long("rule"), argument("GROUP|RULE"))] - rule: Option, + /// Example: `biome lint --skip=correctness/noUnusedVariables --skip=suspicious` + #[bpaf(long("skip"), argument("GROUP|RULE"))] + skip: Vec, /// Use this option when you want to format code piped from `stdin`, and print the output to `stdout`. /// diff --git a/crates/biome_cli/src/execute/mod.rs b/crates/biome_cli/src/execute/mod.rs index a920a145e13f..8a3b55bf87ca 100644 --- a/crates/biome_cli/src/execute/mod.rs +++ b/crates/biome_cli/src/execute/mod.rs @@ -131,7 +131,12 @@ pub enum TraversalMode { /// The option overrides the Biome configuration file as follows: /// - When a rule is passed, its severity level is set to `error' if it is a recommended rule, or `warn' otherwise. /// - When a rule group is passed, the `recommended` flag is enabled, but if the `all` flag is enabled. - rule: Option, + only: Vec, + /// Skip the given rule or rule group. + /// The option overrides the Biome configuration file as follows: + /// - When a rule is passed, its severity level is set to `off'. + /// - When a rule group is passed, all rules of the group are skipped. + skip: Vec, }, /// This mode is enabled when running the command `biome ci` CI { diff --git a/crates/biome_cli/src/execute/process_file/format.rs b/crates/biome_cli/src/execute/process_file/format.rs index 10b35d5d98d6..fbc2be9c40b3 100644 --- a/crates/biome_cli/src/execute/process_file/format.rs +++ b/crates/biome_cli/src/execute/process_file/format.rs @@ -26,7 +26,12 @@ pub(crate) fn format_with_guard<'ctx>( debug!("Pulling diagnostics from parsed file"); let diagnostics_result = workspace_file .guard() - .pull_diagnostics(RuleCategories::SYNTAX, max_diagnostics.into(), None) + .pull_diagnostics( + RuleCategories::SYNTAX, + max_diagnostics.into(), + Vec::new(), + Vec::new(), + ) .with_file_path_and_code( workspace_file.path.display().to_string(), category!("format"), diff --git a/crates/biome_cli/src/execute/process_file/lint.rs b/crates/biome_cli/src/execute/process_file/lint.rs index 29ba6975545f..9bbe4e45a0b3 100644 --- a/crates/biome_cli/src/execute/process_file/lint.rs +++ b/crates/biome_cli/src/execute/process_file/lint.rs @@ -57,17 +57,19 @@ pub(crate) fn lint_with_guard<'ctx>( } let max_diagnostics = ctx.remaining_diagnostics.load(Ordering::Relaxed); - let rule = if let TraversalMode::Lint { rule, .. } = ctx.execution.traversal_mode() { - *rule - } else { - None - }; + let (only, skip) = + if let TraversalMode::Lint { only, skip, .. } = ctx.execution.traversal_mode() { + (only.clone(), skip.clone()) + } else { + (Vec::new(), Vec::new()) + }; let pull_diagnostics_result = workspace_file .guard() .pull_diagnostics( RuleCategories::LINT | RuleCategories::SYNTAX, max_diagnostics.into(), - rule, + only, + skip, ) .with_file_path_and_code( workspace_file.path.display().to_string(), diff --git a/crates/biome_cli/src/execute/std_in.rs b/crates/biome_cli/src/execute/std_in.rs index 8474b54463b3..65503dce7def 100644 --- a/crates/biome_cli/src/execute/std_in.rs +++ b/crates/biome_cli/src/execute/std_in.rs @@ -156,17 +156,18 @@ pub(crate) fn run<'a>( } } - let rule = if let TraversalMode::Lint { rule, .. } = mode.traversal_mode() { - *rule + let (only, skip) = if let TraversalMode::Lint { only, skip, .. } = mode.traversal_mode() { + (only.clone(), skip.clone()) } else { - None + (Vec::new(), Vec::new()) }; if !mode.is_check_apply_unsafe() { let result = workspace.pull_diagnostics(PullDiagnosticsParams { categories: RuleCategories::LINT | RuleCategories::SYNTAX, path: biome_path.clone(), max_diagnostics: mode.max_diagnostics.into(), - rule, + only, + skip, })?; diagnostics.extend(result.diagnostics); } diff --git a/crates/biome_cli/src/lib.rs b/crates/biome_cli/src/lib.rs index 8af0b407cbce..0b66f0c179f4 100644 --- a/crates/biome_cli/src/lib.rs +++ b/crates/biome_cli/src/lib.rs @@ -124,7 +124,8 @@ impl<'app> CliSession<'app> { cli_options, linter_configuration, paths, - rule, + only, + skip, stdin_file_path, vcs_configuration, files_configuration, @@ -145,7 +146,8 @@ impl<'app> CliSession<'app> { cli_options, linter_configuration, paths, - rule, + only, + skip, stdin_file_path, vcs_configuration, files_configuration, diff --git a/crates/biome_cli/tests/commands/lint.rs b/crates/biome_cli/tests/commands/lint.rs index 505c2bb9a164..796c8de7316f 100644 --- a/crates/biome_cli/tests/commands/lint.rs +++ b/crates/biome_cli/tests/commands/lint.rs @@ -131,38 +131,6 @@ fn parse_error() { )); } -#[test] -fn lint_rule_rule_doesnt_exist() { - let mut fs = MemoryFileSystem::default(); - let mut console = BufferConsole::default(); - - let file_path = Path::new("check.js"); - fs.insert(file_path.into(), LINT_ERROR.as_bytes()); - - let result = run_cli( - DynRef::Borrowed(&mut fs), - &mut console, - Args::from( - [ - ("lint"), - "--rule=suspicious/inexistant", - file_path.as_os_str().to_str().unwrap(), - ] - .as_slice(), - ), - ); - - assert!(result.is_err(), "run_cli returned {result:?}"); - - assert_cli_snapshot(SnapshotPayload::new( - module_path!(), - "lint_rule_rule_doesnt_exist", - fs, - console, - result, - )); -} - #[test] fn maximum_diagnostics() { let mut fs = MemoryFileSystem::default(); @@ -3306,7 +3274,39 @@ fn lint_error() { } #[test] -fn lint_rule_missing_group() { +fn lint_only_rule_doesnt_exist() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), LINT_ERROR.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious/inexistant", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_rule_doesnt_exist", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_missing_group() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); @@ -3319,7 +3319,7 @@ fn lint_rule_missing_group() { Args::from( [ ("lint"), - "--rule=noDebugger", + "--only=noDebugger", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3330,7 +3330,7 @@ fn lint_rule_missing_group() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_missing_group", + "lint_only_missing_group", fs, console, result, @@ -3338,7 +3338,7 @@ fn lint_rule_missing_group() { } #[test] -fn lint_rule_filter() { +fn lint_only_rule() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let content = "debugger; delete obj.prop;"; @@ -3352,7 +3352,7 @@ fn lint_rule_filter() { Args::from( [ ("lint"), - "--rule=suspicious/noDebugger", + "--only=suspicious/noDebugger", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3361,7 +3361,7 @@ fn lint_rule_filter() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter", + "lint_only_rule", fs, console, result, @@ -3369,7 +3369,71 @@ fn lint_rule_filter() { } #[test] -fn lint_rule_filter_ignore_suppression_comments() { +fn lint_only_multiple_rules() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious/noDebugger", + "--only=performance/noDelete", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_multiple_rules", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_rule_and_group() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious/noDebugger", + "--only=performance", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_rule_and_group", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_rule_ignore_suppression_comments() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let content = r#" @@ -3387,7 +3451,7 @@ fn lint_rule_filter_ignore_suppression_comments() { Args::from( [ ("lint"), - "--rule=suspicious/noDebugger", + "--only=suspicious/noDebugger", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3396,7 +3460,7 @@ fn lint_rule_filter_ignore_suppression_comments() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_ignore_suppression_comments", + "lint_only_rule_ignore_suppression_comments", fs, console, result, @@ -3404,7 +3468,7 @@ fn lint_rule_filter_ignore_suppression_comments() { } #[test] -fn lint_rule_filter_with_config() { +fn lint_only_rule_with_config() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let config = r#"{ @@ -3437,7 +3501,7 @@ fn lint_rule_filter_with_config() { Args::from( [ ("lint"), - "--rule=style/useNamingConvention", + "--only=style/useNamingConvention", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3446,7 +3510,7 @@ fn lint_rule_filter_with_config() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_with_config", + "lint_only_rule_with_config", fs, console, result, @@ -3454,7 +3518,7 @@ fn lint_rule_filter_with_config() { } #[test] -fn lint_rule_filter_with_recommended_disabled() { +fn lint_only_rule_with_recommended_disabled() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let config = r#"{ @@ -3479,7 +3543,7 @@ fn lint_rule_filter_with_recommended_disabled() { Args::from( [ ("lint"), - "--rule=lint/style/useNamingConvention", + "--only=lint/style/useNamingConvention", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3488,7 +3552,7 @@ fn lint_rule_filter_with_recommended_disabled() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_with_recommended_disabled", + "lint_only_rule_with_recommended_disabled", fs, console, result, @@ -3496,7 +3560,7 @@ fn lint_rule_filter_with_recommended_disabled() { } #[test] -fn lint_rule_filter_with_linter_disabled() { +fn lint_only_rule_with_linter_disabled() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let config = r#"{ @@ -3519,7 +3583,7 @@ fn lint_rule_filter_with_linter_disabled() { Args::from( [ ("lint"), - "--rule=style/useNamingConvention", + "--only=style/useNamingConvention", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3528,7 +3592,7 @@ fn lint_rule_filter_with_linter_disabled() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_with_linter_disabled", + "lint_only_rule_with_linter_disabled", fs, console, result, @@ -3536,7 +3600,7 @@ fn lint_rule_filter_with_linter_disabled() { } #[test] -fn lint_rule_filter_group() { +fn lint_only_group() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let config = r#"{ @@ -3565,7 +3629,7 @@ fn lint_rule_filter_group() { Args::from( [ ("lint"), - "--rule=suspicious", + "--only=suspicious", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3574,7 +3638,7 @@ fn lint_rule_filter_group() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_group", + "lint_only_group", fs, console, result, @@ -3582,7 +3646,7 @@ fn lint_rule_filter_group() { } #[test] -fn lint_rule_filter_nursery_group() { +fn lint_only_nursery_group() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let content = ""; @@ -3596,7 +3660,7 @@ fn lint_rule_filter_nursery_group() { Args::from( [ ("lint"), - "--rule=nursery", + "--only=nursery", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3605,7 +3669,7 @@ fn lint_rule_filter_nursery_group() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_nursery_group", + "lint_only_nursery_group", fs, console, result, @@ -3613,7 +3677,7 @@ fn lint_rule_filter_nursery_group() { } #[test] -fn lint_rule_filter_group_with_disabled_rule() { +fn lint_only_group_with_disabled_rule() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); let config = r#"{ @@ -3642,7 +3706,272 @@ fn lint_rule_filter_group_with_disabled_rule() { Args::from( [ ("lint"), - "--rule=lint/suspicious", + "--only=lint/suspicious", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_group_with_disabled_rule", + fs, + console, + result, + )); +} + +#[test] +fn lint_skip_rule() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--skip=suspicious/noDebugger", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_skip_rule", + fs, + console, + result, + )); +} + +#[test] +fn lint_skip_group_with_enabled_rule() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let config = r#"{ + "linter": { + "rules": { + "suspicious": { + "noDebugger": "error" + } + } + } + }"#; + let content = "debugger; delete obj.prop;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + let config_path = Path::new("biome.json"); + fs.insert(config_path.into(), config.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--skip=suspicious", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_skip_group_with_enabled_rule", + fs, + console, + result, + )); +} + +#[test] +fn lint_skip_multiple_rules() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--skip=suspicious/noDebugger", + "--skip=performance/noDelete", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_skip_multiple_rules", + fs, + console, + result, + )); +} + +#[test] +fn lint_skip_rule_and_group() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--skip=suspicious/noDebugger", + "--skip=performance", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_skip_rule_and_group", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_group_skip_rule() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious", + "--skip=suspicious/noDebugger", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_group_skip_rule", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_rule_skip_group() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = "debugger; delete obj.prop; a === -0;"; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious/noDebugger", + "--skip=suspicious", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_rule_skip_group", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_skip_rule() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = ""; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious/noDebugger", + "--skip=suspicious/noDebugger", + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "lint_only_skip_rule", + fs, + console, + result, + )); +} + +#[test] +fn lint_only_skip_group() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + let content = ""; + + let file_path = Path::new("check.js"); + fs.insert(file_path.into(), content.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("lint"), + "--only=suspicious", + "--skip=suspicious", file_path.as_os_str().to_str().unwrap(), ] .as_slice(), @@ -3651,7 +3980,7 @@ fn lint_rule_filter_group_with_disabled_rule() { assert_cli_snapshot(SnapshotPayload::new( module_path!(), - "lint_rule_filter_group_with_disabled_rule", + "lint_only_skip_group", fs, console, result, diff --git a/crates/biome_cli/tests/snapshots/main_commands_lint/lint_help.snap b/crates/biome_cli/tests/snapshots/main_commands_lint/lint_help.snap index 0be989a08e21..24d8e01c2dfb 100644 --- a/crates/biome_cli/tests/snapshots/main_commands_lint/lint_help.snap +++ b/crates/biome_cli/tests/snapshots/main_commands_lint/lint_help.snap @@ -7,7 +7,8 @@ expression: content ```block Run various checks on a set of files. -Usage: lint [--write] [--unsafe] [--rule=] [--staged] [--changed] [--since=REF] [PATH]... +Usage: lint [--write] [--unsafe] [--only=]... [--skip=]... [--staged] [--changed +] [--since=REF] [PATH]... Set of properties to integrate Biome with a VCS software. --vcs-client-kind= The kind of client. @@ -71,14 +72,18 @@ Available options: --apply Alias for `--write`, writes safe fixes (deprecated, use `--write`) --apply-unsafe Alias for `--write --unsafe`, writes safe and unsafe fixes (deprecated, use `--write --unsafe`) - --rule= Run only the given rule or rule group. + --only= Run only the given rule or rule group. The option overrides the Biome configuration file as follows: - When a rule is passed, its severity level is set to `error' if it is a recommended rule, or `warn' otherwise. - When a rule group is passed, the `recommended` flag is enabled, but if the `all` flag is enabled. - Example: `biome lint --rule=correctness/noUnusedVariables` - Example: `biome lint --rule=suspicious` + Example: `biome lint --only=correctness/noUnusedVariables --only=suspicious` + --skip= Skip the given rule or rule group. + The option overrides the Biome configuration file as follows: + - When a rule is passed, its severity level is set to `off'. + - When a rule group is passed, all rules of the group are skipped. + Example: `biome lint --skip=correctness/noUnusedVariables --skip=suspicious` --stdin-file-path=PATH Use this option when you want to format code piped from `stdin`, and print the output to `stdout`. The file doesn't need to exist on disk, what matters is the extension of diff --git a/crates/biome_cli/tests/snapshots/main_commands_lint/lint_rule_filter_group.snap b/crates/biome_cli/tests/snapshots/main_commands_lint/lint_only_group.snap similarity index 100% rename from crates/biome_cli/tests/snapshots/main_commands_lint/lint_rule_filter_group.snap rename to crates/biome_cli/tests/snapshots/main_commands_lint/lint_only_group.snap diff --git a/crates/biome_cli/tests/snapshots/main_commands_lint/lint_only_group_skip_rule.snap b/crates/biome_cli/tests/snapshots/main_commands_lint/lint_only_group_skip_rule.snap new file mode 100644 index 000000000000..193ee47d2b01 --- /dev/null +++ b/crates/biome_cli/tests/snapshots/main_commands_lint/lint_only_group_skip_rule.snap @@ -0,0 +1,42 @@ +--- +source: crates/biome_cli/tests/snap_test.rs +expression: content +--- +## `check.js` + +```js +debugger; delete obj.prop; a === -0; +``` + +# Termination Message + +```block +lint ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Some errors were emitted while running checks. + + + +``` + +# Emitted Messages + +```block +check.js:1:28 lint/suspicious/noCompareNegZero FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Do not use the === operator to compare against -0. + + > 1 │ debugger; delete obj.prop; a === -0; + │ ^^^^^^^^ + + i Safe fix: Replace -0 with 0 + + 1 │ debugger;·delete·obj.prop;·a·===·-0; + │ - + +``` + +```block +Checked 1 file in