diff --git a/CHANGELOG.md b/CHANGELOG.md index f873406e5fb6..84aaaa942564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,47 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ### Configuration +#### New features + +- Add an rule option `fix` to override the code fix kind of a rule ([#2882](https://github.com/biomejs/biome/issues/2882)). + + A rule can provide a safe or an **unsafe** code **action**. + You can now tune the kind of code actions thanks to the `fix` option. + This rule option takes a value among: + + - `none`: the rule no longer emits code actions. + - `safe`: the rule emits safe code action. + - `unsafe`: the rule emits unsafe code action. + + The following configuration disables the code actions of `noUnusedVariables`, makes the emitted code actions of `style/useConst` and `style/useTemplate` unsafe and safe respectively. + + ```json + { + "linter": { + "rules": { + "correctness": { + "noUnusedVariables": { + "level": "error", + "fix": "none" + }, + "style": { + "useConst": { + "level": "warn", + "fix": "unsafe" + }, + "useTemplate": { + "level": "warn", + "fix": "safe" + } + } + } + } + } + } + ``` + + Contributed by @Conaclos + #### Enhancements - The `javascript.formatter.trailingComma` option is deprecated and renamed to `javascript.formatter.trailingCommas`. The corresponding CLI option `--trailing-comma` is also deprecated and renamed to `--trailing-commas`. Details can be checked in [#2492](https://github.com/biomejs/biome/pull/2492). Contributed by @Sec-ant diff --git a/Cargo.lock b/Cargo.lock index 6219e3d94571..f8bb9e8e52d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,8 @@ name = "biome_analyze" version = "0.5.7" dependencies = [ "biome_console", + "biome_deserialize", + "biome_deserialize_macros", "biome_diagnostics", "biome_rowan", "bitflags 2.5.0", diff --git a/crates/biome_analyze/Cargo.toml b/crates/biome_analyze/Cargo.toml index d80502ced4db..4bd756e3e5da 100644 --- a/crates/biome_analyze/Cargo.toml +++ b/crates/biome_analyze/Cargo.toml @@ -13,18 +13,20 @@ version = "0.5.7" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -biome_console = { workspace = true } -biome_diagnostics = { workspace = true } -biome_rowan = { workspace = true } -bitflags = { workspace = true } -rustc-hash = { workspace = true } -schemars = { workspace = true, optional = true } -serde = { workspace = true, features = ["derive"] } -tracing = { workspace = true } +biome_console = { workspace = true } +biome_deserialize = { workspace = true, optional = true } +biome_deserialize_macros = { workspace = true, optional = true } +biome_diagnostics = { workspace = true } +biome_rowan = { workspace = true } +bitflags = { workspace = true } +rustc-hash = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"], optional = true } +tracing = { workspace = true } [features] -serde = ["schemars"] +serde = ["dep:serde", "dep:schemars", "dep:biome_deserialize", "dep:biome_deserialize_macros"] [lints] workspace = true diff --git a/crates/biome_analyze/src/options.rs b/crates/biome_analyze/src/options.rs index b113f45e17da..f659eda99015 100644 --- a/crates/biome_analyze/src/options.rs +++ b/crates/biome_analyze/src/options.rs @@ -1,18 +1,23 @@ use rustc_hash::FxHashMap; -use crate::{Rule, RuleKey}; +use crate::{FixKind, Rule, RuleKey}; use std::any::{Any, TypeId}; use std::fmt::Debug; use std::path::PathBuf; /// A convenient new type data structure to store the options that belong to a rule #[derive(Debug)] -pub struct RuleOptions((TypeId, Box)); +pub struct RuleOptions(TypeId, Box, Option); impl RuleOptions { + /// Creates a new [RuleOptions] + pub fn new(options: O, fix_kind: Option) -> Self { + Self(TypeId::of::(), Box::new(options), fix_kind) + } + /// It returns the deserialized rule option pub fn value(&self) -> &O { - let (type_id, value) = &self.0; + let RuleOptions(type_id, value, _) = &self; let current_id = TypeId::of::(); debug_assert_eq!(type_id, ¤t_id); // SAFETY: the code should fail when asserting the types. @@ -21,9 +26,8 @@ impl RuleOptions { value.downcast_ref::().unwrap() } - /// Creates a new [RuleOptions] - pub fn new(options: O) -> Self { - Self((TypeId::of::(), Box::new(options))) + pub fn fix_kind(&self) -> Option { + self.2 } } @@ -41,6 +45,10 @@ impl AnalyzerRules { pub fn get_rule_options(&self, rule_key: &RuleKey) -> Option<&O> { self.0.get(rule_key).map(|o| o.value::()) } + + pub fn get_rule_fix_kind(&self, rule_key: &RuleKey) -> Option { + self.0.get(rule_key).and_then(|options| options.fix_kind()) + } } /// A data structured derived from the `biome.json` file @@ -95,6 +103,16 @@ impl AnalyzerOptions { .cloned() } + pub fn rule_fix_kind(&self) -> Option + where + R: Rule + 'static, + R::Options: Clone, + { + self.configuration + .rules + .get_rule_fix_kind(&RuleKey::rule::()) + } + pub fn preferred_quote(&self) -> &PreferredQuote { &self.configuration.preferred_quote } diff --git a/crates/biome_analyze/src/rule.rs b/crates/biome_analyze/src/rule.rs index 7ccee700d03a..424519ed9a0c 100644 --- a/crates/biome_analyze/src/rule.rs +++ b/crates/biome_analyze/src/rule.rs @@ -14,12 +14,12 @@ use biome_diagnostics::{ Visit, }; use biome_rowan::{AstNode, BatchMutation, BatchMutationExt, Language, TextRange}; -use serde::Serialize; use std::cmp::Ordering; use std::fmt::Debug; -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] /// Static metadata containing information about a rule pub struct RuleMetadata { /// It marks if a rule is deprecated, and if so a reason has to be provided. @@ -35,17 +35,29 @@ pub struct RuleMetadata { /// Whether a rule is recommended or not pub recommended: bool, /// The kind of fix - pub fix_kind: Option, + pub fix_kind: FixKind, /// The source URL of the rule pub sources: &'static [RuleSource], /// The source kind of the rule pub source_kind: Option, } -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +#[cfg_attr( + feature = "serde", + derive( + biome_deserialize_macros::Deserializable, + schemars::JsonSchema, + serde::Deserialize, + serde::Serialize + ) +)] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] /// Used to identify the kind of code action emitted by a rule pub enum FixKind { + /// The rule doesn't emit code actions. + #[default] + None, /// The rule emits a code action that is safe to apply. Usually these fixes don't change the semantic of the program. Safe, /// The rule emits a code action that is _unsafe_ to apply. Usually these fixes remove comments, or change @@ -56,23 +68,27 @@ pub enum FixKind { impl Display for FixKind { fn fmt(&self, fmt: &mut biome_console::fmt::Formatter) -> std::io::Result<()> { match self { + FixKind::None => fmt.write_str("None"), FixKind::Safe => fmt.write_str("Safe"), FixKind::Unsafe => fmt.write_str("Unsafe"), } } } -impl From<&FixKind> for Applicability { - fn from(kind: &FixKind) -> Self { - match kind { - FixKind::Safe => Applicability::Always, - FixKind::Unsafe => Applicability::MaybeIncorrect, +impl TryFrom for Applicability { + type Error = &'static str; + fn try_from(value: FixKind) -> Result { + match value { + FixKind::None => Err("The fix kind is None"), + FixKind::Safe => Ok(Applicability::Always), + FixKind::Unsafe => Ok(Applicability::MaybeIncorrect), } } } -#[derive(Debug, Clone, Eq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub enum RuleSource { /// Rules from [Rust Clippy](https://rust-lang.github.io/rust-clippy/master/index.html) Clippy(&'static str), @@ -241,8 +257,9 @@ impl RuleSource { } } -#[derive(Debug, Default, Clone, Copy, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Default, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub enum RuleSourceKind { /// The rule implements the same logic of the source #[default] @@ -271,7 +288,7 @@ impl RuleMetadata { docs, language, recommended: false, - fix_kind: None, + fix_kind: FixKind::None, sources: &[], source_kind: None, } @@ -288,7 +305,7 @@ impl RuleMetadata { } pub const fn fix_kind(mut self, kind: FixKind) -> Self { - self.fix_kind = Some(kind); + self.fix_kind = kind; self } @@ -312,9 +329,8 @@ impl RuleMetadata { pub fn to_applicability(&self) -> Applicability { self.fix_kind - .as_ref() + .try_into() .expect("Fix kind is not set in the rule metadata") - .into() } } diff --git a/crates/biome_analyze/src/signals.rs b/crates/biome_analyze/src/signals.rs index 44aaa3ee51a7..76de3f6421b9 100644 --- a/crates/biome_analyze/src/signals.rs +++ b/crates/biome_analyze/src/signals.rs @@ -367,6 +367,18 @@ where fn actions(&self) -> AnalyzerActionIter> { let globals = self.options.globals(); + let configured_applicability = if let Some(fix_kind) = self.options.rule_fix_kind::() { + match fix_kind { + crate::FixKind::None => { + // The action is disabled + return AnalyzerActionIter::new(vec![]); + } + crate::FixKind::Safe => Some(Applicability::Always), + crate::FixKind::Unsafe => Some(Applicability::MaybeIncorrect), + } + } else { + None + }; let options = self.options.rule_options::().unwrap_or_default(); let ctx = RuleContext::new( &self.query_result, @@ -384,7 +396,7 @@ where if let Some(action) = R::action(&ctx, &self.state) { actions.push(AnalyzerAction { rule_name: Some((::NAME, R::METADATA.name)), - applicability: action.applicability(), + applicability: configured_applicability.unwrap_or(action.applicability()), category: action.category, mutation: action.mutation, message: action.message, diff --git a/crates/biome_cli/src/commands/explain.rs b/crates/biome_cli/src/commands/explain.rs index 3bd14cb528b1..51ffadcf24cf 100644 --- a/crates/biome_cli/src/commands/explain.rs +++ b/crates/biome_cli/src/commands/explain.rs @@ -1,4 +1,4 @@ -use biome_analyze::RuleMetadata; +use biome_analyze::{FixKind, RuleMetadata}; use biome_console::{markup, ConsoleExt}; use biome_service::documentation::Doc; @@ -10,14 +10,17 @@ fn print_rule(session: CliSession, metadata: &RuleMetadata) { "# "{metadata.name}"\n" }); - if let Some(kind) = &metadata.fix_kind { - session.app.console.log(markup! { - "Fix is "{kind}".\n" - }); - } else { - session.app.console.log(markup! { - "No fix available.\n" - }); + match metadata.fix_kind { + FixKind::None => { + session.app.console.log(markup! { + "No fix available.\n" + }); + } + kind => { + session.app.console.log(markup! { + "Fix is "{kind}".\n" + }); + } } let docs = metadata diff --git a/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs index adb0abe61074..5deb47842776 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs @@ -211,6 +211,7 @@ fn migrate_eslint_rule( group.no_restricted_globals = Some(biome_config::RuleConfiguration::WithOptions( biome_config::RuleWithOptions { level: severity.into(), + fix: None, options: Box::new(no_restricted_globals::RestrictedGlobalsOptions { denied_globals: globals.collect(), }), @@ -225,6 +226,7 @@ fn migrate_eslint_rule( group.use_valid_aria_role = Some(biome_config::RuleConfiguration::WithOptions( biome_config::RuleWithOptions { level: severity.into(), + fix: None, options: Box::new((*rule_options).into()), }, )); @@ -239,6 +241,7 @@ fn migrate_eslint_rule( Some(biome_config::RuleConfiguration::WithOptions( biome_config::RuleWithOptions { level: severity.into(), + fix: None, options: rule_options.into(), }, )); @@ -255,6 +258,7 @@ fn migrate_eslint_rule( group.use_naming_convention = Some(biome_config::RuleConfiguration::WithOptions( biome_config::RuleWithOptions { level: severity.into(), + fix: None, options: options.into(), }, )); @@ -266,6 +270,7 @@ fn migrate_eslint_rule( group.use_filenaming_convention = Some( biome_config::RuleConfiguration::WithOptions(biome_config::RuleWithOptions { level: conf.severity().into(), + fix: None, options: Box::new(conf.option_or_default().into()), }), ); diff --git a/crates/biome_configuration/src/linter/mod.rs b/crates/biome_configuration/src/linter/mod.rs index 2004a5fbf542..030ff8bb681d 100644 --- a/crates/biome_configuration/src/linter/mod.rs +++ b/crates/biome_configuration/src/linter/mod.rs @@ -3,7 +3,7 @@ mod rules; pub use crate::linter::rules::Rules; use biome_analyze::options::RuleOptions; -use biome_analyze::RuleFilter; +use biome_analyze::{FixKind, RuleFilter}; use biome_deserialize::{Deserializable, StringSet}; use biome_deserialize::{DeserializableValue, DeserializationDiagnostic, Merge, VisitableType}; use biome_deserialize_macros::{Deserializable, Merge, Partial}; @@ -89,15 +89,6 @@ impl Deserializable for RuleConfiguration { } } -impl FromStr for RuleConfiguration { - type Err = String; - - fn from_str(s: &str) -> Result { - let result = RulePlainConfiguration::from_str(s)?; - Ok(Self::Plain(result)) - } -} - impl RuleConfiguration { pub fn is_err(&self) -> bool { if let Self::WithOptions(rule) = self { @@ -138,16 +129,26 @@ impl RuleConfiguration { // severity doesn't override the options. impl Merge for RuleConfiguration { fn merge_with(&mut self, other: Self) { - *self = match (&self, other) { - (Self::WithOptions(this), Self::Plain(other)) => Self::WithOptions(RuleWithOptions { - level: other, - options: this.options.clone(), - }), - // FIXME: Rule options don't have a `NoneState`, so we can't deep - // merge them yet. For now, if an override specifies options, - // it will still override *all* options. - (_, other) => other, - }; + match self { + RuleConfiguration::Plain(_) => *self = other, + RuleConfiguration::WithOptions(this) => { + match other { + RuleConfiguration::Plain(level) => { + this.level = level; + } + RuleConfiguration::WithOptions(other) => { + *this = RuleWithOptions { + level: other.level, + fix: other.fix.or(this.fix), + // FIXME: Rule options don't have a `NoneState`, so we can't deep + // merge them yet. For now, if an override specifies options, + // it will still override *all* options. + options: other.options, + } + } + } + } + } } } @@ -156,7 +157,7 @@ impl RuleConfiguration { match self { RuleConfiguration::Plain(_) => None, RuleConfiguration::WithOptions(options) => { - Some(RuleOptions::new(options.options.clone())) + Some(RuleOptions::new(options.options.clone(), options.fix)) } } } @@ -200,24 +201,16 @@ pub enum RulePlainConfiguration { Off, } -impl FromStr for RulePlainConfiguration { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "warn" => Ok(Self::Warn), - "error" => Ok(Self::Error), - "off" => Ok(Self::Off), - _ => Err("Invalid configuration for rule".to_string()), - } - } -} - #[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] #[cfg_attr(feature = "schema", derive(JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RuleWithOptions { + /// The severity of the emitted diagnostics by the rule pub level: RulePlainConfiguration, + /// The kind of the code actions emitted by the rule + #[serde(skip_serializing_if = "Option::is_none")] + pub fix: Option, + /// Rule's options pub options: T, } diff --git a/crates/biome_js_analyze/src/lib.rs b/crates/biome_js_analyze/src/lib.rs index 1daabb6c7456..4de399aed6af 100644 --- a/crates/biome_js_analyze/src/lib.rs +++ b/crates/biome_js_analyze/src/lib.rs @@ -268,7 +268,7 @@ mod tests { options.configuration.rules.push_rule( RuleKey::new("nursery", "useHookAtTopLevel"), - RuleOptions::new(HooksOptions { hooks: vec![hook] }), + RuleOptions::new(HooksOptions { hooks: vec![hook] }, None), ); analyze( diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.js b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.js new file mode 100644 index 000000000000..c4ad81e93292 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.js @@ -0,0 +1 @@ +const x = 0; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.js.snap b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.js.snap new file mode 100644 index 000000000000..133b6b458d1b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.js.snap @@ -0,0 +1,22 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalidFixNone.js +--- +# Input +```jsx +const x = 0; +``` + +# Diagnostics +``` +invalidFixNone.js:1:7 lint/correctness/noUnusedVariables ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This variable is unused. + + > 1 │ const x = 0; + │ ^ + + i Unused variables usually are result of incomplete refactoring, typos and other source of bugs. + + +``` diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.options.json b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.options.json new file mode 100644 index 000000000000..8e0ae3239f76 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/invalidFixNone.options.json @@ -0,0 +1,12 @@ +{ + "linter": { + "rules": { + "correctness": { + "noUnusedVariables": { + "level": "warn", + "fix": "none" + } + } + } + } +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.js b/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.js new file mode 100644 index 000000000000..2a846ca5ae46 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.js @@ -0,0 +1 @@ +var foo = `bar`; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.js.snap b/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.js.snap new file mode 100644 index 000000000000..5e6e4b4a1935 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.js.snap @@ -0,0 +1,25 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalidFixSafe.js +--- +# Input +```jsx +var foo = `bar`; +``` + +# Diagnostics +``` +invalidFixSafe.js:1:11 lint/style/noUnusedTemplateLiteral FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Do not use template literals if interpolation and special-character handling are not needed. + + > 1 │ var foo = `bar`; + │ ^^^^^ + + i Safe fix: Replace with string literal + + - var·foo·=·`bar`; + + var·foo·=·"bar"; + + +``` diff --git a/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.options.json b/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.options.json new file mode 100644 index 000000000000..1b77434405bd --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/noUnusedTemplateLiteral/invalidFixSafe.options.json @@ -0,0 +1,12 @@ +{ + "linter": { + "rules": { + "style": { + "noUnusedTemplateLiteral": { + "level": "warn", + "fix": "safe" + } + } + } + } +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.js b/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.js new file mode 100644 index 000000000000..fea5890d55f9 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.js @@ -0,0 +1 @@ +let x = 0; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.js.snap b/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.js.snap new file mode 100644 index 000000000000..967fb0bf645c --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.js.snap @@ -0,0 +1,30 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalidFixUnsafe.js +--- +# Input +```jsx +let x = 0; +``` + +# Diagnostics +``` +invalidFixUnsafe.js:1:1 lint/style/useConst FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This let declares a variable that is only assigned once. + + > 1 │ let x = 0; + │ ^^^ + + i 'x' is never reassigned. + + > 1 │ let x = 0; + │ ^ + + i Unsafe fix: Use const instead. + + - let·x·=·0; + + const·x·=·0; + + +``` diff --git a/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.options.json b/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.options.json new file mode 100644 index 000000000000..3dd35a0c46b6 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/style/useConst/invalidFixUnsafe.options.json @@ -0,0 +1,12 @@ +{ + "linter": { + "rules": { + "style": { + "useConst": { + "level": "warn", + "fix": "unsafe" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 8cef09992af6..7365f8f19f05 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1622,53 +1622,177 @@ export type RuleConfiguration_for_NamingConventionOptions = | RuleWithOptions_for_NamingConventionOptions; export type RulePlainConfiguration = "warn" | "error" | "off"; export interface RuleWithOptions_for_Null { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: null; } export interface RuleWithOptions_for_ValidAriaRoleOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: ValidAriaRoleOptions; } export interface RuleWithOptions_for_ComplexityOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: ComplexityOptions; } export interface RuleWithOptions_for_HooksOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: HooksOptions; } export interface RuleWithOptions_for_DeprecatedHooksOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: DeprecatedHooksOptions; } export interface RuleWithOptions_for_NoCssEmptyBlockOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: NoCssEmptyBlockOptions; } export interface RuleWithOptions_for_RestrictedImportsOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: RestrictedImportsOptions; } export interface RuleWithOptions_for_UtilityClassSortingOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: UtilityClassSortingOptions; } export interface RuleWithOptions_for_RestrictedGlobalsOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: RestrictedGlobalsOptions; } export interface RuleWithOptions_for_ConsistentArrayTypeOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: ConsistentArrayTypeOptions; } export interface RuleWithOptions_for_FilenamingConventionOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: FilenamingConventionOptions; } export interface RuleWithOptions_for_NamingConventionOptions { + /** + * The kind of the code actions emitted by the rule + */ + fix?: FixKind; + /** + * The severity of the emitted diagnostics by the rule + */ level: RulePlainConfiguration; + /** + * Rule's options + */ options: NamingConventionOptions; } +/** + * Used to identify the kind of code action emitted by a rule + */ +export type FixKind = "none" | "safe" | "unsafe"; export interface ValidAriaRoleOptions { allowInvalidRoles: string[]; ignoreNonDom: boolean; diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 3ecb70284906..96341bc4450d 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1018,6 +1018,26 @@ }, "additionalProperties": false }, + "FixKind": { + "description": "Used to identify the kind of code action emitted by a rule", + "oneOf": [ + { + "description": "The rule doesn't emit code actions.", + "type": "string", + "enum": ["none"] + }, + { + "description": "The rule emits a code action that is safe to apply. Usually these fixes don't change the semantic of the program.", + "type": "string", + "enum": ["safe"] + }, + { + "description": "The rule emits a code action that is _unsafe_ to apply. Usually these fixes remove comments, or change the semantic of the program.", + "type": "string", + "enum": ["unsafe"] + } + ] + }, "Format": { "description": "Supported cases.", "type": "string", @@ -2086,8 +2106,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/ComplexityOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/ComplexityOptions" }] + } }, "additionalProperties": false }, @@ -2095,8 +2125,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/ConsistentArrayTypeOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/ConsistentArrayTypeOptions" }] + } }, "additionalProperties": false }, @@ -2104,8 +2144,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/DeprecatedHooksOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/DeprecatedHooksOptions" }] + } }, "additionalProperties": false }, @@ -2113,8 +2163,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/FilenamingConventionOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/FilenamingConventionOptions" }] + } }, "additionalProperties": false }, @@ -2122,8 +2182,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/HooksOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/HooksOptions" }] + } }, "additionalProperties": false }, @@ -2131,8 +2201,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/NamingConventionOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/NamingConventionOptions" }] + } }, "additionalProperties": false }, @@ -2140,8 +2220,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/NoCssEmptyBlockOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/NoCssEmptyBlockOptions" }] + } }, "additionalProperties": false }, @@ -2149,7 +2239,14 @@ "type": "object", "required": ["level"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + } }, "additionalProperties": false }, @@ -2157,8 +2254,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/RestrictedGlobalsOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/RestrictedGlobalsOptions" }] + } }, "additionalProperties": false }, @@ -2166,8 +2273,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/RestrictedImportsOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/RestrictedImportsOptions" }] + } }, "additionalProperties": false }, @@ -2175,8 +2292,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/UtilityClassSortingOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/UtilityClassSortingOptions" }] + } }, "additionalProperties": false }, @@ -2184,8 +2311,18 @@ "type": "object", "required": ["level", "options"], "properties": { - "level": { "$ref": "#/definitions/RulePlainConfiguration" }, - "options": { "$ref": "#/definitions/ValidAriaRoleOptions" } + "fix": { + "description": "The kind of the code actions emitted by the rule", + "anyOf": [{ "$ref": "#/definitions/FixKind" }, { "type": "null" }] + }, + "level": { + "description": "The severity of the emitted diagnostics by the rule", + "allOf": [{ "$ref": "#/definitions/RulePlainConfiguration" }] + }, + "options": { + "description": "Rule's options", + "allOf": [{ "$ref": "#/definitions/ValidAriaRoleOptions" }] + } }, "additionalProperties": false },