Skip to content

Commit 31adde6

Browse files
committed
feat(lint): add noReactDeps
1 parent 7b7ef04 commit 31adde6

File tree

9 files changed

+328
-85
lines changed

9 files changed

+328
-85
lines changed

crates/biome_configuration/src/analyzer/linter/rules.rs

+101-82
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_diagnostics_categories/src/categories.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ define_categories! {
140140
"lint/nursery/noConsole": "https://biomejs.dev/linter/rules/no-console",
141141
"lint/nursery/noConstantBinaryExpression": "https://biomejs.dev/linter/rules/no-constant-binary-expression",
142142
"lint/nursery/noDescendingSpecificity": "https://biomejs.dev/linter/rules/no-descending-specificity",
143+
"lint/nursery/noDestructuredProps": "https://biomejs.dev/linter/rules/no-destructured-props",
143144
"lint/nursery/noDocumentCookie": "https://biomejs.dev/linter/rules/no-document-cookie",
144145
"lint/nursery/noDocumentImportInPage": "https://biomejs.dev/linter/rules/no-document-import-in-page",
145146
"lint/nursery/noDoneCallback": "https://biomejs.dev/linter/rules/no-done-callback",
@@ -170,7 +171,7 @@ define_categories! {
170171
"lint/nursery/noPackagePrivateImports": "https://biomejs.dev/linter/rules/no-package-private-imports",
171172
"lint/nursery/noProcessEnv": "https://biomejs.dev/linter/rules/no-process-env",
172173
"lint/nursery/noProcessGlobal": "https://biomejs.dev/linter/rules/no-process-global",
173-
"lint/nursery/noDestructuredProps": "https://biomejs.dev/linter/rules/no-destructured-props",
174+
"lint/nursery/noReactDeps": "https://biomejs.dev/linter/rules/no-react-deps",
174175
"lint/nursery/noReactSpecificProps": "https://biomejs.dev/linter/rules/no-react-specific-props",
175176
"lint/nursery/noRestrictedImports": "https://biomejs.dev/linter/rules/no-restricted-imports",
176177
"lint/nursery/noRestrictedTypes": "https://biomejs.dev/linter/rules/no-restricted-types",

crates/biome_js_analyze/src/lint/nursery.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod no_octal_escape;
2626
pub mod no_package_private_imports;
2727
pub mod no_process_env;
2828
pub mod no_process_global;
29+
pub mod no_react_deps;
2930
pub mod no_restricted_imports;
3031
pub mod no_restricted_types;
3132
pub mod no_secrets;
@@ -55,4 +56,4 @@ pub mod use_sorted_classes;
5556
pub mod use_strict_mode;
5657
pub mod use_trim_start_end;
5758
pub mod use_valid_autocomplete;
58-
declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_await_in_loop :: NoAwaitInLoop , self :: no_common_js :: NoCommonJs , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_destructured_props :: NoDestructuredProps , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_consistent_object_definition :: UseConsistentObjectDefinition , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } }
59+
declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_await_in_loop :: NoAwaitInLoop , self :: no_common_js :: NoCommonJs , self :: no_constant_binary_expression :: NoConstantBinaryExpression , self :: no_destructured_props :: NoDestructuredProps , self :: no_document_cookie :: NoDocumentCookie , self :: no_document_import_in_page :: NoDocumentImportInPage , self :: no_duplicate_else_if :: NoDuplicateElseIf , self :: no_dynamic_namespace_import_access :: NoDynamicNamespaceImportAccess , self :: no_enum :: NoEnum , self :: no_exported_imports :: NoExportedImports , self :: no_floating_promises :: NoFloatingPromises , self :: no_global_dirname_filename :: NoGlobalDirnameFilename , self :: no_head_element :: NoHeadElement , self :: no_head_import_in_document :: NoHeadImportInDocument , self :: no_img_element :: NoImgElement , self :: no_import_cycles :: NoImportCycles , self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_noninteractive_element_interactions :: NoNoninteractiveElementInteractions , self :: no_octal_escape :: NoOctalEscape , self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_process_global :: NoProcessGlobal , self :: no_react_deps :: NoReactDeps , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , self :: no_secrets :: NoSecrets , self :: no_static_element_interactions :: NoStaticElementInteractions , self :: no_substr :: NoSubstr , self :: no_template_curly_in_string :: NoTemplateCurlyInString , self :: no_ts_ignore :: NoTsIgnore , self :: no_unwanted_polyfillio :: NoUnwantedPolyfillio , self :: no_useless_escape_in_regex :: NoUselessEscapeInRegex , self :: no_useless_string_raw :: NoUselessStringRaw , self :: no_useless_undefined :: NoUselessUndefined , self :: use_adjacent_overload_signatures :: UseAdjacentOverloadSignatures , self :: use_aria_props_supported_by_role :: UseAriaPropsSupportedByRole , self :: use_at_index :: UseAtIndex , self :: use_collapsed_if :: UseCollapsedIf , self :: use_component_export_only_modules :: UseComponentExportOnlyModules , self :: use_consistent_curly_braces :: UseConsistentCurlyBraces , self :: use_consistent_member_accessibility :: UseConsistentMemberAccessibility , self :: use_consistent_object_definition :: UseConsistentObjectDefinition , self :: use_explicit_type :: UseExplicitType , self :: use_exports_last :: UseExportsLast , self :: use_google_font_display :: UseGoogleFontDisplay , self :: use_google_font_preconnect :: UseGoogleFontPreconnect , self :: use_guard_for_in :: UseGuardForIn , self :: use_parse_int_radix :: UseParseIntRadix , self :: use_sorted_classes :: UseSortedClasses , self :: use_strict_mode :: UseStrictMode , self :: use_trim_start_end :: UseTrimStartEnd , self :: use_valid_autocomplete :: UseValidAutocomplete ,] } }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use biome_analyze::{context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleDomain, RuleSource, RuleSourceKind};
2+
use biome_console::markup;
3+
use biome_js_syntax::{JsCallExpression, JsIdentifierBinding};
4+
use biome_rowan::AstNode;
5+
6+
declare_lint_rule! {
7+
/// Disallow usage of dependency arrays in `createEffect` and `createMemo`.
8+
///
9+
/// In Solid, `createEffect` and `createMemo` track dependencies automatically, it's no need to add dependency arrays.
10+
///
11+
/// ## Examples
12+
///
13+
/// ### Invalid
14+
///
15+
/// ```js,expect_diagnostic
16+
/// import { createEffect } from "solid-js";
17+
/// createEffect(() => {
18+
/// console.log(signal());
19+
/// }, [signal()]);
20+
/// ```
21+
///
22+
/// ```js,expect_diagnostic
23+
/// import { createEffect } from "solid-js";
24+
/// createEffect(() => {
25+
/// console.log(signal());
26+
/// }, [signal]);
27+
/// ```
28+
///
29+
/// ```js,expect_diagnostic
30+
/// import { createEffect } from "solid-js";
31+
/// const deps = [signal];
32+
/// createEffect(() => {
33+
/// console.log(signal());
34+
/// }, deps)
35+
/// ```
36+
///
37+
/// ```js,expect_diagnostic
38+
/// import { createMemo } from "solid-js";
39+
/// const value = createMemo(() => computeExpensiveValue(a(), b()), [a(), b()]);
40+
/// ```
41+
///
42+
/// ```js,expect_diagnostic
43+
/// import { createMemo } from "solid-js";
44+
/// const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b]);
45+
/// ```
46+
///
47+
/// ```js,expect_diagnostic
48+
/// import { createMemo } from "solid-js";
49+
/// const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b()]);
50+
/// ```
51+
///
52+
/// ```js,expect_diagnostic
53+
/// import { createMemo } from "solid-js";
54+
/// const deps = [a, b];
55+
/// const value = createMemo(() => computeExpensiveValue(a(), b()), deps);
56+
/// ```
57+
///
58+
/// ```js,expect_diagnostic
59+
/// import { createMemo } from "solid-js";
60+
/// const deps = [a, b];
61+
/// const memoFn = () => computeExpensiveValue(a(), b());
62+
/// const value = createMemo(memoFn, deps);
63+
/// ```
64+
///
65+
/// ### Valid
66+
///
67+
/// ```js
68+
/// import { createEffect } from "solid-js";
69+
/// createEffect(() => {
70+
/// console.log(signal());
71+
/// });
72+
/// ```
73+
///
74+
/// ```js
75+
/// import { createEffect } from "solid-js";
76+
/// createEffect((prev) => {
77+
/// console.log(signal());
78+
/// return prev + 1;
79+
/// }, 0);
80+
/// ```
81+
///
82+
/// ```js
83+
/// import { createEffect } from "solid-js";
84+
/// createEffect((prev) => {
85+
/// console.log(signal());
86+
/// return (prev || 0) + 1;
87+
/// });
88+
/// ```
89+
///
90+
/// ```js
91+
/// import { createEffect } from "solid-js";
92+
/// createEffect((prev) => {
93+
/// console.log(signal());
94+
/// return prev ? prev + 1 : 1;
95+
/// }, undefined);
96+
/// ```
97+
///
98+
/// ```js
99+
/// import { createMemo } from "solid-js";
100+
/// const value = createMemo(() => computeExpensiveValue(a(), b()));
101+
/// ```
102+
///
103+
/// ```js
104+
/// import { createMemo } from "solid-js";
105+
/// const sum = createMemo((prev) => input() + prev, 0);
106+
/// ```
107+
///
108+
/// ```js
109+
/// import { createEffect } from "solid-js";
110+
/// const args = [
111+
/// () => {
112+
/// console.log(signal());
113+
/// },
114+
/// [signal()],
115+
/// ];
116+
/// createEffect(...args);
117+
/// ```
118+
pub NoReactDeps {
119+
version: "next",
120+
name: "noReactDeps",
121+
language: "js",
122+
domains: &[RuleDomain::Solid],
123+
recommended: false,
124+
sources: &[RuleSource::EslintSolid("no-react-deps")],
125+
source_kind: RuleSourceKind::Inspired,
126+
}
127+
}
128+
129+
impl Rule for NoReactDeps {
130+
type Query = Ast<JsCallExpression>;
131+
type State = ();
132+
type Signals = Option<Self::State>;
133+
type Options = ();
134+
135+
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
136+
let _binding = ctx.query();
137+
Some(())
138+
}
139+
140+
fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
141+
let node = ctx.query();
142+
Some(
143+
RuleDiagnostic::new(
144+
rule_category!(),
145+
node.range(),
146+
markup! {
147+
"Variable is read here."
148+
},
149+
)
150+
.note(markup! {
151+
"This note will give you more information."
152+
}),
153+
)
154+
}
155+
}

crates/biome_js_analyze/src/options.rs

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createEffect, createMemo } from "solid-js";
2+
3+
createEffect(() => {
4+
console.log(signal());
5+
}, [signal()]);
6+
7+
createEffect(() => {
8+
console.log(signal());
9+
}, [signal]);
10+
11+
const deps = [signal];
12+
createEffect(() => {
13+
console.log(signal());
14+
}, deps);
15+
16+
const value = createMemo(() => computeExpensiveValue(a(), b()), [a(), b()]);
17+
18+
const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b]);
19+
20+
const value = createMemo(() => computeExpensiveValue(a(), b()), [a, b()]);
21+
22+
const deps = [a, b];
23+
const value = createMemo(() => computeExpensiveValue(a(), b()), deps);
24+
25+
const deps = [a, b];
26+
const memoFn = () => computeExpensiveValue(a(), b());
27+
const value = createMemo(memoFn, deps);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createEffect, createMemo } from "solid-js";
2+
3+
createEffect(() => {
4+
console.log(signal());
5+
});
6+
7+
createEffect((prev) => {
8+
console.log(signal());
9+
return prev + 1;
10+
}, 0);
11+
12+
createEffect((prev) => {
13+
console.log(signal());
14+
return (prev || 0) + 1;
15+
});
16+
17+
createEffect((prev) => {
18+
console.log(signal());
19+
return prev ? prev + 1 : 1;
20+
}, undefined);
21+
22+
const value = createMemo(() => computeExpensiveValue(a(), b()));
23+
24+
const sum = createMemo((prev) => input() + prev, 0);
25+
26+
const args = [() => { console.log(signal()); }, [signal()]];
27+
createEffect(...args);

packages/@biomejs/backend-jsonrpc/src/workspace.ts

+6-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@biomejs/biome/configuration_schema.json

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)