Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ mod jest {
mod react {
pub mod button_has_type;
pub mod checked_requires_onchange_or_readonly;
pub mod exhaustive_deps;
pub mod iframe_missing_sandbox;
pub mod jsx_boolean_value;
pub mod jsx_curly_brace_presence;
Expand Down Expand Up @@ -506,10 +507,6 @@ mod node {
pub mod no_new_require;
}

mod react_hooks {
pub mod exhaustive_deps;
}

oxc_macros::declare_all_lint_rules! {
// import::no_deprecated,
// import::no_unused_modules,
Expand Down Expand Up @@ -795,6 +792,7 @@ oxc_macros::declare_all_lint_rules! {
promise::valid_params,
react::button_has_type,
react::checked_requires_onchange_or_readonly,
react::exhaustive_deps,
react::iframe_missing_sandbox,
react::jsx_boolean_value,
react::jsx_curly_brace_presence,
Expand All @@ -806,8 +804,8 @@ oxc_macros::declare_all_lint_rules! {
react::jsx_no_useless_fragment,
react::jsx_props_no_spread_multi,
react::no_children_prop,
react::no_danger,
react::no_danger_with_children,
react::no_danger,
react::no_direct_mutation_state,
react::no_find_dom_node,
react::no_is_mounted,
Expand Down Expand Up @@ -962,5 +960,4 @@ oxc_macros::declare_all_lint_rules! {
vitest::prefer_to_be_object,
vitest::prefer_to_be_truthy,
vitest::require_local_test_context_for_concurrent_snapshots,
react_hooks::exhaustive_deps,
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ use crate::{
AstNode,
};

const SCOPE: &str = "eslint-plugin-react-hooks";

fn missing_callback_diagnostic(hook_name: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("React hook {hook_name} requires an effect callback."))
.with_label(span)
.with_help("Did you forget to pass a callback to the hook?")
.with_error_code_scope(SCOPE)
}

fn dependency_array_required_diagnostic(hook_name: &str, span: Span) -> OxcDiagnostic {
Expand All @@ -49,12 +52,14 @@ fn unknown_dependencies_diagnostic(hook_name: &str, span: Span) -> OxcDiagnostic
))
.with_help("Pass an inline function instead.")
.with_label(span)
.with_error_code_scope(SCOPE)
}

fn async_effect_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Effect callbacks are synchronous to prevent race conditions.")
.with_label(span)
.with_help("Consider putting the asynchronous code inside a function and calling it from the effect.")
.with_error_code_scope(SCOPE)
}

fn missing_dependency_diagnostic(hook_name: &str, deps: &[String], span: Span) -> OxcDiagnostic {
Expand All @@ -79,6 +84,7 @@ fn missing_dependency_diagnostic(hook_name: &str, deps: &[String], span: Span) -
})
.with_label(span)
.with_help("Either include it or remove the dependency array.")
.with_error_code_scope(SCOPE)
}

fn unnecessary_dependency_diagnostic(hook_name: &str, dep_name: &str, span: Span) -> OxcDiagnostic {
Expand All @@ -92,13 +98,14 @@ fn dependency_array_not_array_literal_diagnostic(hook_name: &str, span: Span) ->
"React Hook {hook_name} was passed a dependency list that is not an array literal. This means we can't statically verify whether you've passed the correct dependencies."
))
.with_label(span)
.with_help("Use an array literal as the second argument.")
.with_help("Use an array literal as the second argument.") .with_error_code_scope(SCOPE)
}

fn literal_in_dependency_array_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("The literal is not a valid dependency because it never changes.")
.with_label(span)
.with_help("Remove the literal from the array.")
.with_error_code_scope(SCOPE)
}

fn complex_expression_in_dependency_array_diagnostic(hook_name: &str, span: Span) -> OxcDiagnostic {
Expand All @@ -107,6 +114,7 @@ fn complex_expression_in_dependency_array_diagnostic(hook_name: &str, span: Span
))
.with_label(span)
.with_help("Extract the expression to a separate variable so it can be statically checked.")
.with_error_code_scope(SCOPE)
}

fn dependency_changes_on_every_render_diagnostic(hook_name: &str, span: Span) -> OxcDiagnostic {
Expand All @@ -115,6 +123,7 @@ fn dependency_changes_on_every_render_diagnostic(hook_name: &str, span: Span) ->
))
.with_label(span)
.with_help("Try memoizing this variable with `useRef` or `useCallback`.")
.with_error_code_scope(SCOPE)
}

fn unnecessary_outer_scope_dependency_diagnostic(
Expand All @@ -127,6 +136,7 @@ fn unnecessary_outer_scope_dependency_diagnostic(
))
.with_label(span)
.with_help("Consider removing it from the dependency array. Outer scope values like aren't valid dependencies because mutating them doesn't re-render the component.")
.with_error_code_scope(SCOPE)
}

fn infinite_rerender_call_to_set_state_diagnostic(hook_name: &str, span: Span) -> OxcDiagnostic {
Expand All @@ -135,12 +145,14 @@ fn infinite_rerender_call_to_set_state_diagnostic(hook_name: &str, span: Span) -
))
.with_label(span)
.with_help("Consider adding an empty list of dependencies to make it clear which values are intended to be stable.")
.with_error_code_scope(SCOPE)
}

fn ref_accessed_directly_in_effect_cleanup_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("The ref's value `.current` is accessed directly in the effect cleanup function.")
.with_label(span)
.with_help("The ref value will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by react, copy it to a variable inside the effect and use that variable in the cleanup function.")
.with_error_code_scope(SCOPE)
}

#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -226,7 +238,7 @@ impl Rule for ExhaustiveDeps {
Argument::SpreadElement(_) => {
ctx.diagnostic(unknown_dependencies_diagnostic(
hook_name.as_str(),
call_expr.callee.span(),
callback_node.span(),
));
None
}
Expand Down Expand Up @@ -312,7 +324,7 @@ impl Rule for ExhaustiveDeps {
_ => {
ctx.diagnostic(unknown_dependencies_diagnostic(
hook_name.as_str(),
call_expr.callee.span(),
callback_node.span(),
));
None
}
Expand Down
Loading