diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c97e2fd1af..06463c79e231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,16 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b - Add [noOctalEscape](https://biomejs.dev/linter/rules/no-octal-escape/). Contributed by @fireairforce +#### Enhancements + +- Add an option `reportUnnecessaryDependencies` to [useExhaustiveDependencies](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/). + + Defaults to true. When set to false, errors will be suppressed for React hooks that declare dependencies but do not use them. + + Contributed by @simon-paris + +- Add an option `reportMissingDependenciesArray` to [useExhaustiveDependencies](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/). Contributed by @simon-paris + #### Bug fixes - [noControlCharactersInRegex](https://www.biomejs.dev/linter/rules/no-control-characters-in-regex) no longer panics on regexes with incomplete escape sequences. Contributed by @Conaclos diff --git a/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs b/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs index 38519499846a..59d4e5518c32 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs @@ -297,6 +297,10 @@ pub struct UseExhaustiveDependenciesOptions { #[serde(default = "report_unnecessary_dependencies_default")] pub report_unnecessary_dependencies: bool, + /// Whether to report an error when a hook has no dependencies array. + #[serde(default)] + pub report_missing_dependencies_array: bool, + /// List of hooks of which the dependencies should be validated. #[serde(default)] #[deserializable(validate = "non_empty")] @@ -307,6 +311,7 @@ impl Default for UseExhaustiveDependenciesOptions { fn default() -> Self { Self { report_unnecessary_dependencies: report_unnecessary_dependencies_default(), + report_missing_dependencies_array: false, hooks: vec![], } } @@ -407,6 +412,8 @@ impl HookConfigMaps { /// Flags the possible fixes that were found pub enum Fix { + /// When the entire dependencies array is missing + MissingDependenciesArray { function_name_range: TextRange }, /// When a dependency needs to be added. AddDependency { function_name_range: TextRange, @@ -749,7 +756,13 @@ impl Rule for UseExhaustiveDependencies { }; if result.dependencies_node.is_none() { - return vec![]; + if options.report_missing_dependencies_array { + return vec![Fix::MissingDependenciesArray { + function_name_range: result.function_name_range, + }]; + } else { + return vec![]; + } } let component_function_range = component_function.text_range(); @@ -904,6 +917,9 @@ impl Rule for UseExhaustiveDependencies { fn instances_for_signal(signal: &Self::State) -> Vec { match signal { + Fix::MissingDependenciesArray { + function_name_range: _, + } => vec![], Fix::AddDependency { captures, .. } => vec![captures.0.clone()], Fix::RemoveDependency { dependencies, .. } => dependencies .iter() @@ -920,6 +936,15 @@ impl Rule for UseExhaustiveDependencies { fn diagnostic(ctx: &RuleContext, dep: &Self::State) -> Option { match dep { + Fix::MissingDependenciesArray { + function_name_range, + } => { + return Some(RuleDiagnostic::new( + rule_category!(), + function_name_range, + markup! {"This hook does not have a dependencies array"}, + )) + } Fix::AddDependency { function_name_range, captures, diff --git a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.js b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.js new file mode 100644 index 000000000000..46c54160c186 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.js @@ -0,0 +1,12 @@ +import {useEffect} from "react"; + +// should not report errors for the unused `b` when the reportMissingDependenciesArray option is false +function ReportMissingDependenciesArray() { + const [a] = useState(1); + + useEffect(() => { + console.log(a); + }); + + return a; +} diff --git a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.js.snap b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.js.snap new file mode 100644 index 000000000000..66e6b299f548 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.js.snap @@ -0,0 +1,36 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: reportMissingDependenciesArray.js +--- +# Input +```jsx +import {useEffect} from "react"; + +// should not report errors for the unused `b` when the reportMissingDependenciesArray option is false +function ReportMissingDependenciesArray() { + const [a] = useState(1); + + useEffect(() => { + console.log(a); + }); + + return a; +} + +``` + +# Diagnostics +``` +reportMissingDependenciesArray.js:7:5 lint/correctness/useExhaustiveDependencies ━━━━━━━━━━━━━━━━━━━ + + ! This hook does not have a dependencies array + + 5 │ const [a] = useState(1); + 6 │ + > 7 │ useEffect(() => { + │ ^^^^^^^^^ + 8 │ console.log(a); + 9 │ }); + + +``` diff --git a/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.options.json b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.options.json new file mode 100644 index 000000000000..aba3b4ecb765 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/reportMissingDependenciesArray.options.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "rules": { + "correctness": { + "useExhaustiveDependencies": { + "level": "error", + "options": { + "reportMissingDependenciesArray": true + } + } + } + } + } +} diff --git a/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap b/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap index 197288d87b84..19c89ebf47bb 100644 --- a/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap +++ b/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap @@ -16,4 +16,5 @@ hooks_incorrect_options.json:9:7 deserialize ━━━━━━━━━━━ i Known keys: - reportUnnecessaryDependencies + - reportMissingDependenciesArray - hooks diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 1adc459d76b6..3f02aed0c016 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -2326,6 +2326,10 @@ export interface UseExhaustiveDependenciesOptions { * List of hooks of which the dependencies should be validated. */ hooks?: Hook[]; + /** + * Whether to report an error when a hook has no dependencies array. + */ + reportMissingDependenciesArray?: boolean; /** * Whether to report an error when a dependency is listed in the dependencies array but isn't used. Defaults to true. */ diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 522ac842bf49..efffec672982 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -4009,6 +4009,11 @@ "type": "array", "items": { "$ref": "#/definitions/Hook" } }, + "reportMissingDependenciesArray": { + "description": "Whether to report an error when a hook has no dependencies array.", + "default": false, + "type": "boolean" + }, "reportUnnecessaryDependencies": { "description": "Whether to report an error when a dependency is listed in the dependencies array but isn't used. Defaults to true.", "default": true,