diff --git a/.changeset/wet-results-talk.md b/.changeset/wet-results-talk.md new file mode 100644 index 000000000000..3f27248faeba --- /dev/null +++ b/.changeset/wet-results-talk.md @@ -0,0 +1,19 @@ +--- +"@biomejs/biome": minor +--- + +Added new capabilities to the CLI arguments `--skip` and `--only`, available to the `biome lint` command. + +`--skip` and `--only` can now accept domain names; when provided, Biome will run or skip all the rules that belong to a certain domain. + +For example, the following command will only run the rules that belong to the [next](https://biomejs.dev/linter/domains/#next) domain: + +```shell +biome lint --only=next +``` + +Another example, the following command will skip the rules that belong to the [project](https://biomejs.dev/linter/domains/#project) domain: + +```shell +biome lint --skip=project +``` diff --git a/crates/biome_analyze/src/rule.rs b/crates/biome_analyze/src/rule.rs index c38f7a8a8b9f..fecfab4d1ecb 100644 --- a/crates/biome_analyze/src/rule.rs +++ b/crates/biome_analyze/src/rule.rs @@ -16,6 +16,7 @@ use biome_rowan::{AstNode, BatchMutation, BatchMutationExt, Language, TextRange} use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::Debug; +use std::str::FromStr; #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -512,6 +513,38 @@ impl RuleDomain { Self::Tailwind => &[], } } + + pub const fn as_str(&self) -> &'static str { + match self { + Self::React => "react", + Self::Test => "test", + Self::Solid => "solid", + Self::Next => "next", + Self::Qwik => "qwik", + Self::Vue => "vue", + Self::Project => "project", + Self::Tailwind => "tailwind", + } + } +} + +impl FromStr for RuleDomain { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "react" => Ok(Self::React), + "test" => Ok(Self::Test), + "solid" => Ok(Self::Solid), + "next" => Ok(Self::Next), + "qwik" => Ok(Self::Qwik), + "vue" => Ok(Self::Vue), + "project" => Ok(Self::Project), + "tailwind" => Ok(Self::Tailwind), + + _ => Err("Invalid rule domain"), + } + } } impl RuleMetadata { diff --git a/crates/biome_cli/src/commands/lint.rs b/crates/biome_cli/src/commands/lint.rs index df20df8bd9da..1992421aaef7 100644 --- a/crates/biome_cli/src/commands/lint.rs +++ b/crates/biome_cli/src/commands/lint.rs @@ -2,7 +2,7 @@ use super::{FixFileModeOptions, determine_fix_file_mode}; use crate::cli_options::CliOptions; use crate::commands::{CommandRunner, get_files_to_process_with_cli_options}; use crate::{CliDiagnostic, Execution, TraversalMode}; -use biome_configuration::analyzer::RuleSelector; +use biome_configuration::analyzer::AnalyzerSelector; use biome_configuration::css::CssLinterConfiguration; use biome_configuration::graphql::GraphqlLinterConfiguration; use biome_configuration::javascript::JsLinterConfiguration; @@ -26,8 +26,8 @@ pub(crate) struct LintCommandPayload { pub(crate) vcs_configuration: Option, pub(crate) files_configuration: Option, pub(crate) paths: Vec, - pub(crate) only: Vec, - pub(crate) skip: Vec, + pub(crate) only: Vec, + pub(crate) skip: Vec, pub(crate) stdin_file_path: Option, pub(crate) staged: bool, pub(crate) changed: bool, diff --git a/crates/biome_cli/src/commands/mod.rs b/crates/biome_cli/src/commands/mod.rs index 442944b119db..2fe10c994fe8 100644 --- a/crates/biome_cli/src/commands/mod.rs +++ b/crates/biome_cli/src/commands/mod.rs @@ -8,7 +8,7 @@ use crate::{ setup_cli_subscriber, }; use biome_configuration::analyzer::assist::AssistEnabled; -use biome_configuration::analyzer::{LinterEnabled, RuleSelector}; +use biome_configuration::analyzer::{AnalyzerSelector, LinterEnabled}; use biome_configuration::css::{CssFormatterConfiguration, CssLinterConfiguration}; use biome_configuration::formatter::FormatterEnabled; use biome_configuration::graphql::{GraphqlFormatterConfiguration, GraphqlLinterConfiguration}; @@ -233,20 +233,28 @@ pub enum BiomeCommand { #[bpaf(external, hide_usage)] cli_options: CliOptions, - /// Run only the given rule or group of rules. + /// Run only the given rule, group of rules or domain. /// If the severity level of a rule is `off`, /// then the severity level of the rule is set to `error` if it is a recommended rule or `warn` otherwise. /// - /// Example: `biome lint --only=correctness/noUnusedVariables --only=suspicious` - #[bpaf(long("only"), argument("GROUP|RULE"))] - only: Vec, + /// Example: + /// + /// ```shell + /// biome lint --only=correctness/noUnusedVariables --only=suspicious --only=test + /// ``` + #[bpaf(long("only"), argument("GROUP|RULE|DOMAIN"))] + only: Vec, - /// Skip the given rule or group of rules by setting the severity level of the rules to `off`. + /// Skip the given rule, group of rules or domain by setting the severity level of the rules to `off`. /// This option takes precedence over `--only`. /// - /// Example: `biome lint --skip=correctness/noUnusedVariables --skip=suspicious` - #[bpaf(long("skip"), argument("GROUP|RULE"))] - skip: Vec, + /// Example: + /// + /// ```shell + /// biome lint --skip=correctness/noUnusedVariables --skip=suspicious --skip=project + /// ``` + #[bpaf(long("skip"), argument("GROUP|RULE|DOMAIN"))] + 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/commands/scan_kind.rs b/crates/biome_cli/src/commands/scan_kind.rs index 8cb1fa62a0a7..01df340a6b77 100644 --- a/crates/biome_cli/src/commands/scan_kind.rs +++ b/crates/biome_cli/src/commands/scan_kind.rs @@ -80,7 +80,8 @@ pub(crate) fn derive_best_scan_kind( mod tests { use super::*; use crate::{TraversalMode, VcsTargeted}; - use biome_configuration::{LinterConfiguration, analyzer::RuleSelector}; + use biome_configuration::LinterConfiguration; + use biome_configuration::analyzer::RuleSelector; #[test] fn should_return_none_for_lint_command() { @@ -88,7 +89,7 @@ mod tests { fix_file_mode: None, stdin: None, only: vec![], - skip: vec![RuleSelector::Rule("correctness", "noPrivateImports")], + skip: vec![RuleSelector::Rule("correctness", "noPrivateImports").into()], vcs_targeted: VcsTargeted::default(), suppress: false, diff --git a/crates/biome_cli/src/execute/mod.rs b/crates/biome_cli/src/execute/mod.rs index 41d5adf90e7a..1a58281e680c 100644 --- a/crates/biome_cli/src/execute/mod.rs +++ b/crates/biome_cli/src/execute/mod.rs @@ -18,7 +18,7 @@ use crate::reporter::terminal::{ConsoleReporter, ConsoleReporterVisitor}; use crate::{ CliDiagnostic, CliSession, DiagnosticsPayload, Reporter, TEMPORARY_INTERNAL_REPORTER_FILE, }; -use biome_configuration::analyzer::RuleSelector; +use biome_configuration::analyzer::AnalyzerSelector; use biome_console::{ConsoleExt, markup}; use biome_diagnostics::{Category, category}; use biome_diagnostics::{Resource, SerdeJsonError}; @@ -126,10 +126,10 @@ pub enum TraversalMode { /// Run only the given rule or group of rules. /// If the severity level of a rule is `off`, /// then the severity level of the rule is set to `error` if it is a recommended rule or `warn` otherwise. - only: Vec, + only: Vec, /// Skip the given rule or group of rules by setting the severity level of the rules to `off`. /// This option takes precedence over `--only`. - skip: Vec, + skip: Vec, /// A flag to know vcs integrated options such as `--staged` or `--changed` are enabled vcs_targeted: VcsTargeted, /// Suppress existing diagnostics with a `// biome-ignore` comment diff --git a/crates/biome_cli/tests/cases/linter_domains.rs b/crates/biome_cli/tests/cases/linter_domains.rs index 237bff6b1907..9d50fe546b47 100644 --- a/crates/biome_cli/tests/cases/linter_domains.rs +++ b/crates/biome_cli/tests/cases/linter_domains.rs @@ -355,3 +355,101 @@ describe("foo", () => { result, )); } + +#[test] +fn should_enable_domain_via_cli() { + let mut console = BufferConsole::default(); + let fs = MemoryFileSystem::default(); + let config = Utf8Path::new("biome.json"); + fs.insert( + config.into(), + r#"{ + "linter": { + "rules": { + "recommended": false + }, + "domains": { + "test": "all" + } + } +} +"# + .as_bytes(), + ); + let test1 = Utf8Path::new("test1.js"); + fs.insert( + test1.into(), + r#"describe.only("bar", () => {}); +"# + .as_bytes(), + ); + + let content = r#" +describe("foo", () => { + beforeEach(() => {}); + beforeEach(() => {}); + test("bar", () => { + someFn(); + }); +}); + "#; + let test2 = Utf8Path::new("test2.js"); + fs.insert(test2.into(), content.as_bytes()); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["lint", "--only=test", test1.as_str(), test2.as_str()].as_slice()), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "should_enable_domain_via_cli", + fs, + console, + result, + )); +} + +#[test] +fn should_disable_domain_via_cli() { + let mut console = BufferConsole::default(); + let fs = MemoryFileSystem::default(); + let test1 = Utf8Path::new("test1.js"); + fs.insert( + test1.into(), + r#"describe.only("bar", () => {}); +"# + .as_bytes(), + ); + + let content = r#" +describe("foo", () => { + beforeEach(() => {}); + beforeEach(() => {}); + test("bar", () => { + someFn(); + }); +}); + "#; + let test2 = Utf8Path::new("test2.js"); + fs.insert(test2.into(), content.as_bytes()); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["lint", "--skip=test", test1.as_str(), test2.as_str()].as_slice()), + ); + + assert!(result.is_ok(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "should_disable_domain_via_cli", + fs, + console, + result, + )); +} diff --git a/crates/biome_cli/tests/snapshots/main_cases_linter_domains/should_disable_domain_via_cli.snap b/crates/biome_cli/tests/snapshots/main_cases_linter_domains/should_disable_domain_via_cli.snap new file mode 100644 index 000000000000..4f07f34d06b7 --- /dev/null +++ b/crates/biome_cli/tests/snapshots/main_cases_linter_domains/should_disable_domain_via_cli.snap @@ -0,0 +1,30 @@ +--- +source: crates/biome_cli/tests/snap_test.rs +expression: redactor(content) +--- +## `test1.js` + +```js +describe.only("bar", () => {}); + +``` + +## `test2.js` + +```js + +describe("foo", () => { + beforeEach(() => {}); + beforeEach(() => {}); + test("bar", () => { + someFn(); + }); +}); + +``` + +# Emitted Messages + +```block +Checked 2 files in